Large update to get the new theme engine up and running.

This commit is contained in:
Leon Styhre 2022-01-29 18:41:22 +01:00
parent 0ca2b62edf
commit c35a297d9a
22 changed files with 1436 additions and 489 deletions

View file

@ -642,6 +642,7 @@ int main(int argc, char* argv[])
AudioManager::getInstance(); AudioManager::getInstance();
MameNames::getInstance(); MameNames::getInstance();
ThemeData::getThemeSets();
loadSystemsReturnCode loadSystemsStatus = loadSystemConfigFile(); loadSystemsReturnCode loadSystemsStatus = loadSystemConfigFile();
if (loadSystemsStatus) { if (loadSystemsStatus) {

View file

@ -16,6 +16,7 @@
#include "Window.h" #include "Window.h"
#include "components/BadgeComponent.h" #include "components/BadgeComponent.h"
#include "components/DateTimeComponent.h" #include "components/DateTimeComponent.h"
#include "components/LottieComponent.h"
#include "components/RatingComponent.h" #include "components/RatingComponent.h"
#include "components/ScrollableContainer.h" #include "components/ScrollableContainer.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"

View file

@ -45,7 +45,7 @@ void GamelistView::legacyPopulateFields()
// Thumbnails. // Thumbnails.
mImageComponents.push_back(std::make_unique<ImageComponent>()); mImageComponents.push_back(std::make_unique<ImageComponent>());
mImageComponents.back()->setMetadataField("md_thumbnail"); mImageComponents.back()->setMetadataField("image_md_thumbnail");
mImageComponents.back()->setOrigin(0.5f, 0.5f); mImageComponents.back()->setOrigin(0.5f, 0.5f);
mImageComponents.back()->setVisible(false); mImageComponents.back()->setVisible(false);
mImageComponents.back()->setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f); mImageComponents.back()->setMaxSize(mSize.x * (0.25f - 2.0f * padding), mSize.y * 0.10f);
@ -54,7 +54,7 @@ void GamelistView::legacyPopulateFields()
// Marquee. // Marquee.
mImageComponents.push_back(std::make_unique<ImageComponent>()); mImageComponents.push_back(std::make_unique<ImageComponent>());
mImageComponents.back()->setMetadataField("md_marquee"); mImageComponents.back()->setMetadataField("image_md_marquee");
mImageComponents.back()->setOrigin(0.5f, 0.5f); mImageComponents.back()->setOrigin(0.5f, 0.5f);
mImageComponents.back()->setVisible(false); mImageComponents.back()->setVisible(false);
mImageComponents.back()->setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f); mImageComponents.back()->setMaxSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.18f);
@ -63,7 +63,7 @@ void GamelistView::legacyPopulateFields()
// Image. // Image.
mImageComponents.push_back(std::make_unique<ImageComponent>()); mImageComponents.push_back(std::make_unique<ImageComponent>());
mImageComponents.back()->setMetadataField("md_image"); mImageComponents.back()->setMetadataField("image_md_image");
mImageComponents.back()->setOrigin(0.5f, 0.5f); mImageComponents.back()->setOrigin(0.5f, 0.5f);
mImageComponents.back()->setPosition(mSize.x * 0.25f, mImageComponents.back()->setPosition(mSize.x * 0.25f,
mList.getPosition().y + mSize.y * 0.2125f); mList.getPosition().y + mSize.y * 0.2125f);
@ -74,9 +74,10 @@ void GamelistView::legacyPopulateFields()
if (mViewStyle == ViewController::VIDEO) { if (mViewStyle == ViewController::VIDEO) {
// Video. // Video.
mVideoComponents.push_back(std::make_unique<VideoFFmpegComponent>()); mVideoComponents.push_back(std::make_unique<VideoFFmpegComponent>());
mVideoComponents.back()->setMetadataField("md_video"); mVideoComponents.back()->setMetadataField("video_md_video");
mVideoComponents.back()->setOrigin(0.5f, 0.5f); mVideoComponents.back()->setOrigin(0.5f, 0.5f);
mVideoComponents.back()->setPosition(mSize.x * 0.25f, mSize.y * 0.4f); mVideoComponents.back()->setPosition(mSize.x * 0.25f,
mList.getPosition().y + mSize.y * 0.2125f);
mVideoComponents.back()->setSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.4f); mVideoComponents.back()->setSize(mSize.x * (0.5f - 2.0f * padding), mSize.y * 0.4f);
mVideoComponents.back()->setDefaultZIndex(30.0f); mVideoComponents.back()->setDefaultZIndex(30.0f);
addChild(mVideoComponents.back().get()); addChild(mVideoComponents.back().get());
@ -85,84 +86,85 @@ void GamelistView::legacyPopulateFields()
mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y); mList.setPosition(mSize.x * (0.50f + padding), mList.getPosition().y);
mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y); mList.setSize(mSize.x * (0.50f - padding), mList.getSize().y);
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT); mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { legacyUpdateInfoPanel(); }); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
// Metadata labels + values. // Metadata labels + values.
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Rating: ", false); mTextComponents.back()->setText("Rating: ", false);
mTextComponents.back()->setMetadataField("md_lbl_rating"); mTextComponents.back()->setMetadataField("text_md_lbl_rating");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Released: ", false); mTextComponents.back()->setText("Released: ", false);
mTextComponents.back()->setMetadataField("md_lbl_releasedate"); mTextComponents.back()->setMetadataField("text_md_lbl_releasedate");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Developer: ", false); mTextComponents.back()->setText("Developer: ", false);
mTextComponents.back()->setMetadataField("md_lbl_developer"); mTextComponents.back()->setMetadataField("text_md_lbl_developer");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Publisher: ", false); mTextComponents.back()->setText("Publisher: ", false);
mTextComponents.back()->setMetadataField("md_lbl_publisher"); mTextComponents.back()->setMetadataField("text_md_lbl_publisher");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Genre: ", false); mTextComponents.back()->setText("Genre: ", false);
mTextComponents.back()->setMetadataField("md_lbl_genre"); mTextComponents.back()->setMetadataField("text_md_lbl_genre");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Players: ", false); mTextComponents.back()->setText("Players: ", false);
mTextComponents.back()->setMetadataField("md_lbl_players"); mTextComponents.back()->setMetadataField("text_md_lbl_players");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Last played: ", false); mTextComponents.back()->setText("Last played: ", false);
mTextComponents.back()->setMetadataField("md_lbl_lastplayed"); mTextComponents.back()->setMetadataField("text_md_lbl_lastplayed");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setText("Times played: ", false); mTextComponents.back()->setText("Times played: ", false);
mTextComponents.back()->setMetadataField("md_lbl_playcount"); mTextComponents.back()->setMetadataField("text_md_lbl_playcount");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mRatingComponents.push_back(std::make_unique<RatingComponent>()); mRatingComponents.push_back(std::make_unique<RatingComponent>());
mRatingComponents.back()->setMetadataField("md_rating"); mRatingComponents.back()->setMetadataField("rating_md_rating");
mRatingComponents.back()->setDefaultZIndex(40.0f); mRatingComponents.back()->setDefaultZIndex(40.0f);
addChild(mRatingComponents.back().get()); addChild(mRatingComponents.back().get());
mDateTimeComponents.push_back(std::make_unique<DateTimeComponent>()); mDateTimeComponents.push_back(std::make_unique<DateTimeComponent>());
mDateTimeComponents.back()->setMetadataField("md_releasedate"); mDateTimeComponents.back()->setMetadataField("datetime_md_releasedate");
addChild(mDateTimeComponents.back().get()); addChild(mDateTimeComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_developer"); mTextComponents.back()->setMetadataField("text_md_developer");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_publisher"); mTextComponents.back()->setMetadataField("text_md_publisher");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_genre"); mTextComponents.back()->setMetadataField("text_md_genre");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_players"); mTextComponents.back()->setMetadataField("text_md_players");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mDateTimeComponents.push_back(std::make_unique<DateTimeComponent>()); mDateTimeComponents.push_back(std::make_unique<DateTimeComponent>());
mDateTimeComponents.back()->setMetadataField("md_lastplayed"); mDateTimeComponents.back()->setMetadataField("datetime_md_lastplayed");
mDateTimeComponents.back()->setDisplayRelative(true);
addChild(mDateTimeComponents.back().get()); addChild(mDateTimeComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_playcount"); mTextComponents.back()->setMetadataField("text_md_playcount");
addChild(mTextComponents.back().get()); addChild(mTextComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setMetadataField("md_name"); mTextComponents.back()->setMetadataField("text_md_name");
mTextComponents.back()->setPosition(mSize.x, mSize.y); mTextComponents.back()->setPosition(mSize.x, mSize.y);
mTextComponents.back()->setFont(Font::get(FONT_SIZE_MEDIUM)); mTextComponents.back()->setFont(Font::get(FONT_SIZE_MEDIUM));
mTextComponents.back()->setHorizontalAlignment(ALIGN_CENTER); mTextComponents.back()->setHorizontalAlignment(ALIGN_CENTER);
@ -172,7 +174,7 @@ void GamelistView::legacyPopulateFields()
// Badges. // Badges.
mBadgeComponents.push_back(std::make_unique<BadgeComponent>()); mBadgeComponents.push_back(std::make_unique<BadgeComponent>());
mBadgeComponents.back()->setMetadataField("md_badges"); mBadgeComponents.back()->setMetadataField("badges_md_badges");
mBadgeComponents.back()->setOrigin(0.5f, 0.5f); mBadgeComponents.back()->setOrigin(0.5f, 0.5f);
mBadgeComponents.back()->setPosition(mSize.x * 0.8f, mSize.y * 0.7f); mBadgeComponents.back()->setPosition(mSize.x * 0.8f, mSize.y * 0.7f);
mBadgeComponents.back()->setSize(mSize.x * 0.15f, mSize.y * 0.2f); mBadgeComponents.back()->setSize(mSize.x * 0.15f, mSize.y * 0.2f);
@ -180,22 +182,21 @@ void GamelistView::legacyPopulateFields()
addChild(mBadgeComponents.back().get()); addChild(mBadgeComponents.back().get());
// Scrollable container (game description). // Scrollable container (game description).
mScrollableContainerComponents.push_back(std::make_unique<ScrollableContainer>()); mContainerComponents.push_back(std::make_unique<ScrollableContainer>());
mScrollableContainerComponents.back()->setMetadataField("md_description"); mContainerComponents.back()->setMetadataField("text_md_description");
mScrollableContainerComponents.back()->setSize( mContainerComponents.back()->setSize(mSize.x * (0.50f - 2.0f * padding),
mSize.x * (0.50f - 2.0f * padding), mSize.y - mContainerComponents.back()->getPosition().y);
mSize.y - mScrollableContainerComponents.back()->getPosition().y); mContainerComponents.back()->setAutoScroll(true);
mScrollableContainerComponents.back()->setAutoScroll(true); mContainerComponents.back()->setDefaultZIndex(40.0f);
mScrollableContainerComponents.back()->setDefaultZIndex(40.0f); addChild(mContainerComponents.back().get());
addChild(mScrollableContainerComponents.back().get());
mTextComponents.push_back(std::make_unique<TextComponent>()); mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setFont(Font::get(FONT_SIZE_SMALL)); mTextComponents.back()->setFont(Font::get(FONT_SIZE_SMALL));
mTextComponents.back()->setSize(mScrollableContainerComponents.back()->getSize().x, 0.0f); mTextComponents.back()->setSize(mContainerComponents.back()->getSize().x, 0.0f);
mScrollableContainerComponents.back()->addChild(mTextComponents.back().get()); mContainerComponents.back()->addChild(mTextComponents.back().get());
mGamelistInfoComponents.push_back(std::make_unique<TextComponent>()); mGamelistInfoComponents.push_back(std::make_unique<TextComponent>());
mGamelistInfoComponents.back()->setMetadataField("gamelistInfo"); mGamelistInfoComponents.back()->setMetadataField("text_gamelistInfo");
mGamelistInfoComponents.back()->setOrigin(0.5f, 0.5f); mGamelistInfoComponents.back()->setOrigin(0.5f, 0.5f);
mGamelistInfoComponents.back()->setFont(Font::get(FONT_SIZE_SMALL)); mGamelistInfoComponents.back()->setFont(Font::get(FONT_SIZE_SMALL));
mGamelistInfoComponents.back()->setDefaultZIndex(50.0f); mGamelistInfoComponents.back()->setDefaultZIndex(50.0f);
@ -209,9 +210,9 @@ void GamelistView::legacyOnThemeChanged(const std::shared_ptr<ThemeData>& theme)
using namespace ThemeFlags; using namespace ThemeFlags;
mTextComponents[LOGOTEXT]->applyTheme(theme, getName(), "logoText", ALL); mTextComponents[LOGOTEXT]->applyTheme(theme, getName(), "text_logoText", ALL);
mImageComponents[LOGO]->applyTheme(theme, getName(), "logo", ALL); mImageComponents[LOGO]->applyTheme(theme, getName(), "image_logo", ALL);
mImageComponents[BACKGROUND]->applyTheme(theme, getName(), "background", ALL); mImageComponents[BACKGROUND]->applyTheme(theme, getName(), "image_background", ALL);
// Remove old theme extras. // Remove old theme extras.
for (auto extra : mThemeExtras) { for (auto extra : mThemeExtras) {
@ -225,12 +226,13 @@ void GamelistView::legacyOnThemeChanged(const std::shared_ptr<ThemeData>& theme)
for (auto extra : mThemeExtras) for (auto extra : mThemeExtras)
addChild(extra); addChild(extra);
mList.applyTheme(theme, getName(), "gamelist", ALL); mList.applyTheme(theme, getName(), "textlist_gamelist", ALL);
mImageComponents[LegacyImage::MD_THUMBNAIL]->applyTheme( mImageComponents[LegacyImage::MD_THUMBNAIL]->applyTheme(
theme, getName(), mImageComponents[LegacyImage::MD_THUMBNAIL]->getMetadataField(), ALL); theme, getName(), mImageComponents[LegacyImage::MD_THUMBNAIL]->getMetadataField(), ALL);
mImageComponents[LegacyImage::MD_MARQUEE]->applyTheme( mImageComponents[LegacyImage::MD_MARQUEE]->applyTheme(theme, getName(), "image_md_marquee",
theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX |
ROTATION | VISIBLE);
if (mViewStyle == ViewController::DETAILED) { if (mViewStyle == ViewController::DETAILED) {
mImageComponents[LegacyImage::MD_IMAGE]->applyTheme( mImageComponents[LegacyImage::MD_IMAGE]->applyTheme(
@ -276,15 +278,15 @@ void GamelistView::legacyOnThemeChanged(const std::shared_ptr<ThemeData>& theme)
ALL); ALL);
} }
for (auto& container : mScrollableContainerComponents) { for (auto& container : mContainerComponents) {
container->applyTheme(theme, getName(), container->getMetadataField(), container->applyTheme(theme, getName(), container->getMetadataField(),
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
} }
mTextComponents[LegacyText::MD_DESCRIPTION]->setSize( mTextComponents[LegacyText::MD_DESCRIPTION]->setSize(mContainerComponents.front()->getSize().x,
mScrollableContainerComponents.front()->getSize().x, 0.0f); 0.0f);
mTextComponents[LegacyText::MD_DESCRIPTION]->applyTheme( mTextComponents[LegacyText::MD_DESCRIPTION]->applyTheme(
theme, getName(), "md_description", theme, getName(), "text_md_description",
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION)); ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
for (auto& gamelistInfo : mGamelistInfoComponents) for (auto& gamelistInfo : mGamelistInfoComponents)
@ -299,10 +301,10 @@ void GamelistView::legacyOnThemeChanged(const std::shared_ptr<ThemeData>& theme)
// Hide some components if we're in Basic mode. // Hide some components if we're in Basic mode.
if (mViewStyle == ViewController::BASIC) { if (mViewStyle == ViewController::BASIC) {
if (mTheme->getElement(getName(), "logoText", "text") == nullptr) if (mTheme->getElement(getName(), "text_logoText", "text") == nullptr)
mTextComponents[LegacyText::LOGOTEXT]->setVisible(false); mTextComponents[LegacyText::LOGOTEXT]->setVisible(false);
mImageComponents[LegacyImage::MD_IMAGE]->setVisible(false); mImageComponents[LegacyImage::MD_IMAGE]->setVisible(false);
for (auto& container : mScrollableContainerComponents) for (auto& container : mContainerComponents)
container->setVisible(false); container->setVisible(false);
} }
@ -339,11 +341,12 @@ void GamelistView::legacyUpdateInfoPanel()
// If we're scrolling, hide the metadata fields if the last game had this options set, // If we're scrolling, hide the metadata fields if the last game had this options set,
// or if we're in the grouped custom collection view. // or if we're in the grouped custom collection view.
if (mList.isScrolling()) if (mList.isScrolling()) {
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") || if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
(mLastUpdated->getSystem()->isCustomCollection() && (mLastUpdated->getSystem()->isCustomCollection() &&
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName())) mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
hideMetaDataFields = true; hideMetaDataFields = true;
}
if (hideMetaDataFields || mViewStyle == ViewController::BASIC) { if (hideMetaDataFields || mViewStyle == ViewController::BASIC) {
for (size_t i = LegacyText::MD_LBL_RATING; i < LegacyText::MD_DESCRIPTION; ++i) for (size_t i = LegacyText::MD_LBL_RATING; i < LegacyText::MD_DESCRIPTION; ++i)
@ -484,7 +487,7 @@ void GamelistView::legacyUpdateInfoPanel()
} }
mTextComponents[LegacyText::MD_DESCRIPTION]->setText(file->metadata.get("desc")); mTextComponents[LegacyText::MD_DESCRIPTION]->setText(file->metadata.get("desc"));
for (auto& container : mScrollableContainerComponents) for (auto& container : mContainerComponents)
container->reset(); container->reset();
for (auto& rating : mRatingComponents) for (auto& rating : mRatingComponents)
@ -540,66 +543,6 @@ void GamelistView::legacyUpdateInfoPanel()
} }
} }
for (auto& text : mTextComponents) {
if (text->getValue() != "")
continue;
std::string metadata = text->getMetadataField();
if (metadata == "")
continue;
if (metadata == "md_controller") {
std::string controller =
BadgeComponent::getDisplayName(file->metadata.get("controller"));
text->setValue(controller == "unknown" ? "" : controller);
continue;
}
if (metadata == "md_name")
text->setValue(file->metadata.get("name"));
else if (metadata == "md_rating")
text->setValue(mRatingComponents.front()->getRatingValue());
else if (metadata == "md_developer")
text->setValue(file->metadata.get("developer"));
else if (metadata == "md_publisher")
text->setValue(file->metadata.get("publisher"));
else if (metadata == "md_genre")
text->setValue(file->metadata.get("genre"));
else if (metadata == "md_players")
text->setValue(file->metadata.get("players"));
else if (metadata == "md_favorite")
text->setValue(file->metadata.get("favorite") == "true" ? "yes" : "no");
else if (metadata == "md_completed")
text->setValue(file->metadata.get("completed") == "true" ? "yes" : "no");
else if (metadata == "md_kidgame")
text->setValue(file->metadata.get("kidgame") == "true" ? "yes" : "no");
else if (metadata == "md_broken")
text->setValue(file->metadata.get("broken") == "true" ? "yes" : "no");
else if (metadata == "md_playcount")
text->setValue(file->metadata.get("playcount"));
else if (metadata == "md_altemulator")
text->setValue(file->metadata.get("altemulator"));
else
text->setValue(metadata);
}
for (auto& date : mDateTimeComponents) {
std::string metadata = date->getMetadataField();
if (metadata == "")
continue;
if (metadata == "md_releasedate") {
date->setValue(file->metadata.get("releasedate"));
}
else if (metadata == "md_lastplayed") {
date->setValue(file->metadata.get("lastplayed"));
date->setDisplayRelative(true);
}
else {
date->setValue("19700101T000000");
}
}
fadingOut = false; fadingOut = false;
} }
@ -756,10 +699,10 @@ void GamelistView::legacyInitMDValues()
mRatingComponents.front()->setPosition(Renderer::getScreenWidth() * 2.0f, mRatingComponents.front()->setPosition(Renderer::getScreenWidth() * 2.0f,
Renderer::getScreenHeight() * 2.0f); Renderer::getScreenHeight() * 2.0f);
mScrollableContainerComponents.front()->setPosition(Renderer::getScreenWidth() * 2.0f, mContainerComponents.front()->setPosition(Renderer::getScreenWidth() * 2.0f,
Renderer::getScreenHeight() * 2.0f); Renderer::getScreenHeight() * 2.0f);
for (auto& container : mScrollableContainerComponents) { for (auto& container : mContainerComponents) {
container->setPosition(container->getPosition().x, bottom + mSize.y * 0.01f); container->setPosition(container->getPosition().x, bottom + mSize.y * 0.01f);
container->setSize(container->getSize().x, mSize.y - container->getPosition().y); container->setSize(container->getSize().x, mSize.y - container->getPosition().y);
} }

View file

@ -13,17 +13,14 @@
#include "UIModeController.h" #include "UIModeController.h"
#include "animations/LambdaAnimation.h" #include "animations/LambdaAnimation.h"
// #define FADE_IN_START_OPACITY 0.5f #define FADE_IN_START_OPACITY 0.5f
// #define FADE_IN_TIME 325 #define FADE_IN_TIME 325
GamelistView::GamelistView(FileData* root, bool legacyMode) GamelistView::GamelistView(FileData* root)
: GamelistBase {root} : GamelistBase {root}
, mLegacyMode {legacyMode} , mLegacyMode {false}
, mViewStyle {ViewController::BASIC} , mViewStyle {ViewController::BASIC}
{ {
// TEMPORARY
mLegacyMode = true;
mViewStyle = ViewController::getInstance()->getState().viewstyle; mViewStyle = ViewController::getInstance()->getState().viewstyle;
if (mLegacyMode) if (mLegacyMode)
@ -71,8 +68,14 @@ void GamelistView::onFileChanged(FileData* file, bool reloadGamelist)
void GamelistView::onShow() void GamelistView::onShow()
{ {
// Reset any Lottie animations. // Reset any Lottie animations.
for (auto& animation : mLottieAnimComponents)
animation->resetFileAnimation();
// Reset any Lottie animations.
if (mLegacyMode) {
for (auto extra : mThemeExtras) for (auto extra : mThemeExtras)
extra->resetFileAnimation(); extra->resetFileAnimation();
}
mLastUpdated = nullptr; mLastUpdated = nullptr;
GuiComponent::onShow(); GuiComponent::onShow();
@ -84,6 +87,8 @@ void GamelistView::onShow()
void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme) void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
{ {
mLegacyMode = mTheme->isLegacyTheme();
if (mLegacyMode) { if (mLegacyMode) {
legacyOnThemeChanged(theme); legacyOnThemeChanged(theme);
return; return;
@ -91,21 +96,94 @@ void GamelistView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
using namespace ThemeFlags; using namespace ThemeFlags;
// Remove old theme extras. if (mTheme->hasView("gamelist")) {
for (auto extra : mThemeExtras) { for (auto& element : mTheme->getViewElements("gamelist").elements) {
removeChild(extra); if (element.second.type == "image") {
delete extra; mImageComponents.push_back(std::make_unique<ImageComponent>());
mImageComponents.back()->setDefaultZIndex(30.0f);
mImageComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
if (mImageComponents.back()->getMetadataField() != "")
mImageComponents.back()->setScrollHide(true);
addChild(mImageComponents.back().get());
}
else if (element.second.type == "video") {
mVideoComponents.push_back(std::make_unique<VideoFFmpegComponent>());
mVideoComponents.back()->setDefaultZIndex(30.0f);
addChild(mVideoComponents.back().get());
mVideoComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
if (mVideoComponents.back()->getMetadataField() != "")
mVideoComponents.back()->setScrollHide(true);
}
else if (element.second.type == "animation") {
mLottieAnimComponents.push_back(std::make_unique<LottieComponent>());
mLottieAnimComponents.back()->setDefaultZIndex(35.0f);
mLottieAnimComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
addChild(mLottieAnimComponents.back().get());
}
else if (element.second.type == "badges") {
mBadgeComponents.push_back(std::make_unique<BadgeComponent>());
mBadgeComponents.back()->setDefaultZIndex(35.0f);
mBadgeComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
mBadgeComponents.back()->setScrollHide(true);
addChild(mBadgeComponents.back().get());
}
else if (element.second.type == "text") {
if (element.second.has("container") && element.second.get<bool>("container")) {
mContainerTextComponents.push_back(std::make_unique<TextComponent>());
mContainerTextComponents.back()->setDefaultZIndex(40.0f);
mContainerComponents.push_back(std::make_unique<ScrollableContainer>());
mContainerComponents.back()->setAutoScroll(true);
mContainerComponents.back()->addChild(mContainerTextComponents.back().get());
mContainerComponents.back()->applyTheme(theme, "gamelist", element.first,
POSITION | ThemeFlags::SIZE | Z_INDEX |
VISIBLE);
mContainerTextComponents.back()->applyTheme(theme, "gamelist", element.first,
ALL ^ POSITION ^ Z_INDEX ^
ThemeFlags::SIZE ^ VISIBLE);
mContainerTextComponents.back()->setSize(
mContainerComponents.back()->getSize().x, 0.0f);
mContainerComponents.back()->setDefaultZIndex(
mContainerTextComponents.back()->getDefaultZIndex());
mContainerComponents.back()->setZIndex(
mContainerTextComponents.back()->getZIndex());
mContainerComponents.back()->setScrollHide(true);
addChild(mContainerComponents.back().get());
}
else {
mTextComponents.push_back(std::make_unique<TextComponent>());
mTextComponents.back()->setDefaultZIndex(40.0f);
mTextComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
if (mTextComponents.back()->getMetadataField() != "")
mTextComponents.back()->setScrollHide(true);
addChild(mTextComponents.back().get());
}
}
else if (element.second.type == "datetime") {
mDateTimeComponents.push_back(std::make_unique<DateTimeComponent>());
mDateTimeComponents.back()->setDefaultZIndex(40.0f);
mDateTimeComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
if (mDateTimeComponents.back()->getMetadataField() != "")
mDateTimeComponents.back()->setScrollHide(true);
addChild(mDateTimeComponents.back().get());
}
else if (element.second.type == "gamelistinfo") {
mGamelistInfoComponents.push_back(std::make_unique<TextComponent>());
mGamelistInfoComponents.back()->setDefaultZIndex(45.0f);
mGamelistInfoComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
addChild(mGamelistInfoComponents.back().get());
}
else if (element.second.type == "rating") {
mRatingComponents.push_back(std::make_unique<RatingComponent>());
mRatingComponents.back()->setDefaultZIndex(45.0f);
mRatingComponents.back()->applyTheme(theme, "gamelist", element.first, ALL);
mRatingComponents.back()->setScrollHide(true);
addChild(mRatingComponents.back().get());
}
}
} }
mThemeExtras.clear();
// Add new theme extras. mList.setDefaultZIndex(50.0f);
mThemeExtras = ThemeData::makeExtras(theme, getName()); mList.applyTheme(theme, "gamelist", "textlist_gamelist", ALL);
for (auto extra : mThemeExtras)
addChild(extra);
mList.applyTheme(theme, getName(), "gamelist", ALL);
// TODO: Implement logic to populate component vectors.
sortChildren(); sortChildren();
} }
@ -117,6 +195,23 @@ void GamelistView::update(int deltaTime)
return; return;
} }
if (ViewController::getInstance()->getGameLaunchTriggered()) {
for (auto& image : mImageComponents) {
if (image->isAnimationPlaying(0))
image->finishAnimation(0);
}
}
for (auto& video : mVideoComponents) {
if (!mVideoPlaying)
video->onHide();
else if (mVideoPlaying && !video->isVideoPaused() && !mWindow->isScreensaverActive())
video->onShow();
if (ViewController::getInstance()->getGameLaunchTriggered() && video->isAnimationPlaying(0))
video->finishAnimation(0);
}
updateChildren(deltaTime); updateChildren(deltaTime);
} }
@ -140,7 +235,10 @@ void GamelistView::render(const glm::mat4& parentTrans)
HelpStyle GamelistView::getHelpStyle() HelpStyle GamelistView::getHelpStyle()
{ {
HelpStyle style; HelpStyle style;
if (mLegacyMode)
style.applyTheme(mTheme, getName()); style.applyTheme(mTheme, getName());
else
style.applyTheme(mTheme, "gamelist");
return style; return style;
} }
@ -193,6 +291,11 @@ std::vector<HelpPrompt> GamelistView::getHelpPrompts()
void GamelistView::updateInfoPanel() void GamelistView::updateInfoPanel()
{ {
if (mLegacyMode) {
legacyUpdateInfoPanel();
return;
}
FileData* file {(mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected()}; FileData* file {(mList.size() == 0 || mList.isScrolling()) ? nullptr : mList.getSelected()};
// If the game data has already been rendered to the info panel, then skip it this time. // If the game data has already been rendered to the info panel, then skip it this time.
@ -219,8 +322,438 @@ void GamelistView::updateInfoPanel()
} }
} }
if (hideMetaDataFields) // If we're scrolling, hide the metadata fields if the last game had this options set,
; // or if we're in the grouped custom collection view.
if (mList.isScrolling()) {
if ((mLastUpdated && mLastUpdated->metadata.get("hidemetadata") == "true") ||
(mLastUpdated->getSystem()->isCustomCollection() &&
mLastUpdated->getPath() == mLastUpdated->getSystem()->getName()))
hideMetaDataFields = true;
}
// TODO: Implement gamelist logic. if (hideMetaDataFields) {
for (auto& text : mTextComponents) {
if (text->getMetadataField() != "")
text->setVisible(false);
}
for (auto& date : mDateTimeComponents)
date->setVisible(false);
for (auto& badge : mBadgeComponents)
badge->setVisible(false);
for (auto& rating : mRatingComponents)
rating->setVisible(false);
for (auto& cText : mContainerTextComponents) {
if (cText->getMetadataField() != "md_description")
cText->setVisible(false);
}
}
else {
for (auto& text : mTextComponents) {
if (text->getMetadataField() != "")
text->setVisible(true);
}
for (auto& date : mDateTimeComponents)
date->setVisible(true);
for (auto& badge : mBadgeComponents)
badge->setVisible(true);
for (auto& rating : mRatingComponents)
rating->setVisible(true);
for (auto& cText : mContainerTextComponents) {
if (cText->getMetadataField() != "md_description")
cText->setVisible(true);
}
}
bool fadingOut = false;
if (file == nullptr) {
mVideoPlaying = false;
fadingOut = true;
}
else {
// If we're browsing a grouped custom collection, then update the folder metadata
// which will generate a description of three random games and return a pointer to
// the first of these so that we can display its game media.
if (file->getSystem()->isCustomCollection() &&
file->getPath() == file->getSystem()->getName()) {
mRandomGame = CollectionSystemsManager::getInstance()->updateCollectionFolderMetadata(
file->getSystem());
if (mRandomGame) {
for (auto& image : mImageComponents) {
if (image->getMetadataField() == "md_image")
image->setImage(mRandomGame->getImagePath());
else if (image->getMetadataField() == "md_miximage")
image->setImage(mRandomGame->getMiximagePath());
else if (image->getMetadataField() == "md_marquee")
image->setImage(mRandomGame->getMarqueePath(), false, true);
else if (image->getMetadataField() == "md_screenshot")
image->setImage(mRandomGame->getScreenshotPath());
else if (image->getMetadataField() == "md_titlescreen")
image->setImage(mRandomGame->getTitleScreenPath());
else if (image->getMetadataField() == "md_cover")
image->setImage(mRandomGame->getCoverPath());
else if (image->getMetadataField() == "md_backcover")
image->setImage(mRandomGame->getBackCoverPath());
else if (image->getMetadataField() == "md_3dbox")
image->setImage(mRandomGame->get3DBoxPath());
else if (image->getMetadataField() == "md_fanart")
image->setImage(mRandomGame->getFanArtPath());
else if (image->getMetadataField() == "md_thumbnail")
image->setImage(mRandomGame->getThumbnailPath());
}
for (auto& video : mVideoComponents) {
if (video->getMetadataField() == "md_image")
video->setImage(mRandomGame->getImagePath());
else if (video->getMetadataField() == "md_miximage")
video->setImage(mRandomGame->getMiximagePath());
else if (video->getMetadataField() == "md_marquee")
video->setImage(mRandomGame->getMarqueePath(), false, true);
else if (video->getMetadataField() == "md_screenshot")
video->setImage(mRandomGame->getScreenshotPath());
else if (video->getMetadataField() == "md_titlescreen")
video->setImage(mRandomGame->getTitleScreenPath());
else if (video->getMetadataField() == "md_cover")
video->setImage(mRandomGame->getCoverPath());
else if (video->getMetadataField() == "md_backcover")
video->setImage(mRandomGame->getBackCoverPath());
else if (video->getMetadataField() == "md_3dbox")
video->setImage(mRandomGame->get3DBoxPath());
else if (video->getMetadataField() == "md_fanart")
video->setImage(mRandomGame->getFanArtPath());
else if (video->getMetadataField() == "md_thumbnail")
video->setImage(mRandomGame->getThumbnailPath());
// Always stop the video before setting a new video as it will otherwise
// continue to play if it has the same path (i.e. it is the same physical
// video file) as the previously set video. That may happen when entering a
// folder with the same name as the first game file inside, or as in this
// case, when entering a custom collection.
video->onHide();
if (video->hasStaticVideo())
video->setStaticVideo();
else if (!video->setVideo(mRandomGame->getVideoPath()))
video->setDefaultVideo();
}
}
else {
for (auto& image : mImageComponents) {
if (image->getMetadataField() != "")
image->setImage("");
}
for (auto& video : mVideoComponents) {
video->setImage("");
video->setVideo("");
if (video->hasStaticVideo()) {
video->onStopVideo();
video->setStaticVideo();
}
else {
video->setDefaultVideo();
}
}
}
}
else {
for (auto& image : mImageComponents) {
if (image->getMetadataField() == "md_image")
image->setImage(file->getImagePath());
else if (image->getMetadataField() == "md_miximage")
image->setImage(file->getMiximagePath());
else if (image->getMetadataField() == "md_marquee")
image->setImage(file->getMarqueePath(), false, true);
else if (image->getMetadataField() == "md_screenshot")
image->setImage(file->getScreenshotPath());
else if (image->getMetadataField() == "md_titlescreen")
image->setImage(file->getTitleScreenPath());
else if (image->getMetadataField() == "md_cover")
image->setImage(file->getCoverPath());
else if (image->getMetadataField() == "md_backcover")
image->setImage(file->getBackCoverPath());
else if (image->getMetadataField() == "md_3dbox")
image->setImage(file->get3DBoxPath());
else if (image->getMetadataField() == "md_fanart")
image->setImage(file->getFanArtPath());
else if (image->getMetadataField() == "md_thumbnail")
image->setImage(file->getThumbnailPath());
}
for (auto& video : mVideoComponents) {
if (video->getMetadataField() == "md_image")
video->setImage(file->getImagePath());
else if (video->getMetadataField() == "md_miximage")
video->setImage(file->getMiximagePath());
else if (video->getMetadataField() == "md_marquee")
video->setImage(file->getMarqueePath(), false, true);
else if (video->getMetadataField() == "md_screenshot")
video->setImage(file->getScreenshotPath());
else if (video->getMetadataField() == "md_titlescreen")
video->setImage(file->getTitleScreenPath());
else if (video->getMetadataField() == "md_cover")
video->setImage(file->getCoverPath());
else if (video->getMetadataField() == "md_backcover")
video->setImage(file->getBackCoverPath());
else if (video->getMetadataField() == "md_3dbox")
video->setImage(file->get3DBoxPath());
else if (video->getMetadataField() == "md_fanart")
video->setImage(file->getFanArtPath());
else if (video->getMetadataField() == "md_thumbnail")
video->setImage(file->getThumbnailPath());
video->onHide();
if (video->hasStaticVideo())
video->setStaticVideo();
else if (!video->setVideo(file->getVideoPath()))
video->setDefaultVideo();
}
}
mVideoPlaying = true;
// Populate the gamelistInfo field which shows an icon if a folder has been entered
// as well as the game count for the entire system (total and favorites separately).
// If a filter has been applied, then the number of filtered and total games replaces
// the game counter.
for (auto& gamelistInfo : mGamelistInfoComponents) {
std::string gamelistInfoString;
Alignment infoAlign = gamelistInfo->getHorizontalAlignment();
if (mIsFolder && infoAlign == ALIGN_RIGHT)
gamelistInfoString = ViewController::FOLDER_CHAR + " ";
if (mIsFiltered) {
if (mFilteredGameCountAll == mFilteredGameCount)
gamelistInfoString += ViewController::FILTER_CHAR + " " +
std::to_string(mFilteredGameCount) + " / " +
std::to_string(mGameCount);
else
gamelistInfoString +=
ViewController::FILTER_CHAR + " " + std::to_string(mFilteredGameCount) +
" + " + std::to_string(mFilteredGameCountAll - mFilteredGameCount) + " / " +
std::to_string(mGameCount);
}
else {
gamelistInfoString +=
ViewController::CONTROLLER_CHAR + " " + std::to_string(mGameCount);
if (!(file->getSystem()->isCollection() &&
file->getSystem()->getFullName() == "favorites"))
gamelistInfoString += " " + ViewController::FAVORITE_CHAR + " " +
std::to_string(mFavoritesGameCount);
}
if (mIsFolder && infoAlign != ALIGN_RIGHT)
gamelistInfoString += " " + ViewController::FOLDER_CHAR;
gamelistInfo->setValue(gamelistInfoString);
}
// Fade in the game image.
for (auto& image : mImageComponents) {
if (image->getMetadataField() == "md_image") {
auto func = [&image](float t) {
image->setOpacity(static_cast<unsigned char>(
glm::mix(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
};
image->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
}
}
// Fade in the static image.
for (auto& video : mVideoComponents) {
if (video->getMetadataField() == "md_video") {
auto func = [&video](float t) {
video->setOpacity(static_cast<unsigned char>(
glm::mix(static_cast<float>(FADE_IN_START_OPACITY), 1.0f, t) * 255));
};
video->setAnimation(new LambdaAnimation(func, FADE_IN_TIME), 0, nullptr, false);
}
}
for (auto& container : mContainerComponents)
container->reset();
for (auto& rating : mRatingComponents)
rating->setValue(file->metadata.get("rating"));
// Populate the badge slots based on game metadata.
std::vector<BadgeComponent::BadgeInfo> badgeSlots;
for (auto& badgeComponent : mBadgeComponents) {
for (auto& badge : badgeComponent->getBadgeTypes()) {
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(badgeInfo);
}
else {
if (file->metadata.get(badge).compare("true") == 0)
badgeSlots.push_back(badgeInfo);
}
}
badgeComponent->setBadges(badgeSlots);
}
for (auto& text : mTextComponents) {
if (text->getMetadataField() == "md_name")
text->setText(file->metadata.get("name"));
}
if (file->getType() == GAME) {
if (!hideMetaDataFields) {
for (auto& date : mDateTimeComponents) {
if (date->getMetadataField() == "md_lastplayed")
date->setValue(file->metadata.get("lastplayed"));
else if (date->getMetadataField() == "md_playcount")
date->setValue(file->metadata.get("playcount"));
}
}
else if (file->getType() == FOLDER) {
if (!hideMetaDataFields) {
for (auto& date : mDateTimeComponents) {
if (date->getMetadataField() == "md_lastplayed") {
date->setValue(file->metadata.get("lastplayed"));
date->setVisible(false);
date->setVisible(false);
}
}
}
}
}
std::string metadata;
auto getMetadataValue = [&file, &metadata]() -> std::string {
if (metadata == "md_name")
return file->metadata.get("name");
else if (metadata == "md_description")
return file->metadata.get("desc");
else if (metadata == "md_developer")
return file->metadata.get("developer");
else if (metadata == "md_publisher")
return file->metadata.get("publisher");
else if (metadata == "md_genre")
return file->metadata.get("genre");
else if (metadata == "md_players")
return file->metadata.get("players");
else if (metadata == "md_favorite")
return file->metadata.get("favorite") == "true" ? "Yes" : "No";
else if (metadata == "md_completed")
return file->metadata.get("completed") == "true" ? "Yes" : "No";
else if (metadata == "md_kidgame")
return file->metadata.get("kidgame") == "true" ? "Yes" : "No";
else if (metadata == "md_broken")
return file->metadata.get("broken") == "true" ? "Yes" : "No";
else if (metadata == "md_playcount")
return file->metadata.get("playcount");
else if (metadata == "md_altemulator")
return file->metadata.get("altemulator");
else
return metadata;
};
for (auto& text : mContainerTextComponents) {
metadata = text->getMetadataField();
if (metadata == "")
continue;
if (metadata == "md_rating") {
text->setValue(mRatingComponents.front()->getRatingValue());
continue;
}
else if (metadata == "md_controller") {
std::string controller =
BadgeComponent::getDisplayName(file->metadata.get("controller"));
text->setValue(controller == "unknown" ? "" : controller);
continue;
}
text->setValue(getMetadataValue());
}
for (auto& text : mTextComponents) {
metadata = text->getMetadataField();
if (metadata == "")
continue;
if (metadata == "md_rating") {
text->setValue(mRatingComponents.front()->getRatingValue());
continue;
}
else if (metadata == "md_controller") {
std::string controller =
BadgeComponent::getDisplayName(file->metadata.get("controller"));
text->setValue(controller == "unknown" ? "" : controller);
continue;
}
text->setValue(getMetadataValue());
}
for (auto& date : mDateTimeComponents) {
std::string metadata = date->getMetadataField();
if (metadata == "")
continue;
if (metadata == "md_releasedate") {
date->setValue(file->metadata.get("releasedate"));
}
else if (metadata == "md_lastplayed") {
date->setValue(file->metadata.get("lastplayed"));
date->setDisplayRelative(true);
}
else {
date->setValue("19700101T000000");
}
}
fadingOut = false;
}
std::vector<GuiComponent*> comps;
for (auto& text : mTextComponents) {
if (text->getScrollHide())
comps.emplace_back(text.get());
}
for (auto& date : mDateTimeComponents) {
if (date->getScrollHide())
comps.emplace_back(date.get());
}
for (auto& image : mImageComponents) {
if (image->getScrollHide())
comps.emplace_back(image.get());
}
for (auto& badge : mBadgeComponents) {
if (badge->getScrollHide())
comps.emplace_back(badge.get());
}
for (auto& rating : mRatingComponents) {
if (rating->getScrollHide())
comps.emplace_back(rating.get());
}
for (auto& container : mContainerComponents) {
if (container->getScrollHide())
comps.emplace_back(container.get());
}
for (auto it = comps.cbegin(); it != comps.cend(); ++it) {
GuiComponent* comp = *it;
// An animation is playing, then animate if reverse != fadingOut.
// An animation is not playing, then animate if opacity != our target opacity.
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
auto func = [comp](float t) {
comp->setOpacity(static_cast<unsigned char>(glm::mix(0.0f, 1.0f, t) * 255));
};
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
}
}
} }

View file

@ -17,7 +17,7 @@
class GamelistView : public GamelistBase class GamelistView : public GamelistBase
{ {
public: public:
GamelistView(FileData* root, bool legacyMode = false); GamelistView(FileData* root);
~GamelistView(); ~GamelistView();
// Called when a FileData* is added, has its metadata changed, or is removed. // Called when a FileData* is added, has its metadata changed, or is removed.
@ -76,9 +76,11 @@ private:
std::vector<std::unique_ptr<DateTimeComponent>> mDateTimeComponents; std::vector<std::unique_ptr<DateTimeComponent>> mDateTimeComponents;
std::vector<std::unique_ptr<ImageComponent>> mImageComponents; std::vector<std::unique_ptr<ImageComponent>> mImageComponents;
std::vector<std::unique_ptr<VideoFFmpegComponent>> mVideoComponents; std::vector<std::unique_ptr<VideoFFmpegComponent>> mVideoComponents;
std::vector<std::unique_ptr<LottieComponent>> mLottieAnimComponents;
std::vector<std::unique_ptr<BadgeComponent>> mBadgeComponents; std::vector<std::unique_ptr<BadgeComponent>> mBadgeComponents;
std::vector<std::unique_ptr<RatingComponent>> mRatingComponents; std::vector<std::unique_ptr<RatingComponent>> mRatingComponents;
std::vector<std::unique_ptr<ScrollableContainer>> mScrollableContainerComponents; std::vector<std::unique_ptr<ScrollableContainer>> mContainerComponents;
std::vector<std::unique_ptr<TextComponent>> mContainerTextComponents;
std::vector<std::unique_ptr<TextComponent>> mGamelistInfoComponents; std::vector<std::unique_ptr<TextComponent>> mGamelistInfoComponents;
enum LegacyText { enum LegacyText {

View file

@ -76,7 +76,8 @@ void SystemView::populate()
glm::vec3 offsetLogoPlaceholderText = {0.0f, 0.0f, 0.0f}; glm::vec3 offsetLogoPlaceholderText = {0.0f, 0.0f, 0.0f};
// Make logo. // Make logo.
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image"); const ThemeData::ThemeElement* logoElem =
theme->getElement("system", "image_logo", "image");
if (logoElem) { if (logoElem) {
auto path = logoElem->get<std::string>("path"); auto path = logoElem->get<std::string>("path");
std::string defaultPath = std::string defaultPath =
@ -86,7 +87,8 @@ void SystemView::populate()
ResourceManager::getInstance().fileExists(defaultPath))) { ResourceManager::getInstance().fileExists(defaultPath))) {
auto* logo = new ImageComponent(false, false); auto* logo = new ImageComponent(false, false);
logo->setMaxSize(glm::round(mCarousel.logoSize * mCarousel.logoScale)); logo->setMaxSize(glm::round(mCarousel.logoSize * mCarousel.logoScale));
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR); logo->applyTheme(theme, "system", "image_logo",
ThemeFlags::PATH | ThemeFlags::COLOR);
logo->setRotateByTargetSize(true); logo->setRotateByTargetSize(true);
e.data.logo = std::shared_ptr<GuiComponent>(logo); e.data.logo = std::shared_ptr<GuiComponent>(logo);
} }
@ -100,7 +102,7 @@ void SystemView::populate()
glm::vec3 center {resolution.x / 2.0f, resolution.y / 2.0f, 1.0f}; glm::vec3 center {resolution.x / 2.0f, resolution.y / 2.0f, 1.0f};
// Placeholder Image. // Placeholder Image.
logoElem = theme->getElement("system", "logoPlaceholderImage", "image"); logoElem = theme->getElement("system", "image_logoPlaceholderImage", "image");
if (logoElem) { if (logoElem) {
auto path = logoElem->get<std::string>("path"); auto path = logoElem->get<std::string>("path");
std::string defaultPath = std::string defaultPath =
@ -109,7 +111,8 @@ void SystemView::populate()
(!defaultPath.empty() && (!defaultPath.empty() &&
ResourceManager::getInstance().fileExists(defaultPath))) { ResourceManager::getInstance().fileExists(defaultPath))) {
auto* logo = new ImageComponent(false, false); auto* logo = new ImageComponent(false, false);
logo->applyTheme(theme, "system", "logoPlaceholderImage", ThemeFlags::ALL); logo->applyTheme(theme, "system", "image_logoPlaceholderImage",
ThemeFlags::ALL);
if (!logoElem->has("size")) if (!logoElem->has("size"))
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale); logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale);
offsetLogo = logo->getPosition() - center; offsetLogo = logo->getPosition() - center;
@ -120,7 +123,7 @@ void SystemView::populate()
// Placeholder Text. // Placeholder Text.
const ThemeData::ThemeElement* logoPlaceholderText = const ThemeData::ThemeElement* logoPlaceholderText =
theme->getElement("system", "logoPlaceholderText", "text"); theme->getElement("system", "text_logoPlaceholderText", "text");
if (logoPlaceholderText) { if (logoPlaceholderText) {
// Element 'logoPlaceholderText' found in theme: place text // Element 'logoPlaceholderText' found in theme: place text
auto* text = new TextComponent(it->getName(), Font::get(FONT_SIZE_LARGE), auto* text = new TextComponent(it->getName(), Font::get(FONT_SIZE_LARGE),
@ -134,7 +137,7 @@ void SystemView::populate()
text->setHorizontalAlignment(ALIGN_CENTER); text->setHorizontalAlignment(ALIGN_CENTER);
text->setVerticalAlignment(mCarousel.logoAlignment); text->setVerticalAlignment(mCarousel.logoAlignment);
} }
text->applyTheme(it->getTheme(), "system", "logoPlaceholderText", text->applyTheme(it->getTheme(), "system", "text_logoPlaceholderText",
ThemeFlags::ALL); ThemeFlags::ALL);
if (!e.data.logo) { if (!e.data.logo) {
e.data.logo = std::shared_ptr<GuiComponent>(text); e.data.logo = std::shared_ptr<GuiComponent>(text);
@ -541,15 +544,16 @@ void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
return; return;
const ThemeData::ThemeElement* carouselElem = const ThemeData::ThemeElement* carouselElem =
theme->getElement("system", "systemcarousel", "carousel"); theme->getElement("system", "carousel_systemcarousel", "carousel");
if (carouselElem) if (carouselElem)
getCarouselFromTheme(carouselElem); getCarouselFromTheme(carouselElem);
const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text"); const ThemeData::ThemeElement* sysInfoElem =
theme->getElement("system", "text_systemInfo", "text");
if (sysInfoElem) if (sysInfoElem)
mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL); mSystemInfo.applyTheme(theme, "system", "text_systemInfo", ThemeFlags::ALL);
mViewNeedsReload = false; mViewNeedsReload = false;
} }

View file

@ -11,6 +11,8 @@
#include "resources/Font.h" #include "resources/Font.h"
#define PREFIX "button_"
HelpStyle::HelpStyle() HelpStyle::HelpStyle()
{ {
position = position =
@ -32,7 +34,7 @@ HelpStyle::HelpStyle()
void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view) void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view)
{ {
auto elem = theme->getElement(view, "help", "helpsystem"); auto elem = theme->getElement(view, "helpsystem_help", "helpsystem");
if (!elem) if (!elem)
return; return;
@ -73,74 +75,76 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
textStyle = elem->get<std::string>("textStyle"); textStyle = elem->get<std::string>("textStyle");
// Load custom button icons. // Load custom button icons.
// The names may look a bit strange when combined with the PREFIX string "button_" but it's
// because ThemeData adds this prefix to avoid name collisions when using XML attributes.
// General. // General.
if (elem->has("dpad_updown")) if (elem->has(PREFIX "dpad_updown"))
mCustomButtons.dpad_updown = elem->get<std::string>("dpad_updown"); mCustomButtons.dpad_updown = elem->get<std::string>(PREFIX "dpad_updown");
if (elem->has("dpad_leftright")) if (elem->has(PREFIX "dpad_leftright"))
mCustomButtons.dpad_leftright = elem->get<std::string>("dpad_leftright"); mCustomButtons.dpad_leftright = elem->get<std::string>(PREFIX "dpad_leftright");
if (elem->has("dpad_all")) if (elem->has(PREFIX "dpad_all"))
mCustomButtons.dpad_all = elem->get<std::string>("dpad_all"); mCustomButtons.dpad_all = elem->get<std::string>(PREFIX "dpad_all");
if (elem->has("thumbstick_click")) if (elem->has(PREFIX "thumbstick_click"))
mCustomButtons.thumbstick_click = elem->get<std::string>("thumbstick_click"); mCustomButtons.thumbstick_click = elem->get<std::string>(PREFIX "thumbstick_click");
if (elem->has("button_l")) if (elem->has(PREFIX "button_l"))
mCustomButtons.button_l = elem->get<std::string>("button_l"); mCustomButtons.button_l = elem->get<std::string>(PREFIX "button_l");
if (elem->has("button_r")) if (elem->has(PREFIX "button_r"))
mCustomButtons.button_r = elem->get<std::string>("button_r"); mCustomButtons.button_r = elem->get<std::string>(PREFIX "button_r");
if (elem->has("button_lr")) if (elem->has(PREFIX "button_lr"))
mCustomButtons.button_lr = elem->get<std::string>("button_lr"); mCustomButtons.button_lr = elem->get<std::string>(PREFIX "button_lr");
if (elem->has("button_lt")) if (elem->has(PREFIX "button_lt"))
mCustomButtons.button_lt = elem->get<std::string>("button_lt"); mCustomButtons.button_lt = elem->get<std::string>(PREFIX "button_lt");
if (elem->has("button_rt")) if (elem->has(PREFIX "button_rt"))
mCustomButtons.button_rt = elem->get<std::string>("button_rt"); mCustomButtons.button_rt = elem->get<std::string>(PREFIX "button_rt");
// SNES. // SNES.
if (elem->has("button_a_SNES")) if (elem->has(PREFIX "button_a_SNES"))
mCustomButtons.button_a_SNES = elem->get<std::string>("button_a_SNES"); mCustomButtons.button_a_SNES = elem->get<std::string>(PREFIX "button_a_SNES");
if (elem->has("button_b_SNES")) if (elem->has(PREFIX "button_b_SNES"))
mCustomButtons.button_b_SNES = elem->get<std::string>("button_b_SNES"); mCustomButtons.button_b_SNES = elem->get<std::string>(PREFIX "button_b_SNES");
if (elem->has("button_x_SNES")) if (elem->has(PREFIX "button_x_SNES"))
mCustomButtons.button_x_SNES = elem->get<std::string>("button_x_SNES"); mCustomButtons.button_x_SNES = elem->get<std::string>(PREFIX "button_x_SNES");
if (elem->has("button_y_SNES")) if (elem->has(PREFIX "button_y_SNES"))
mCustomButtons.button_y_SNES = elem->get<std::string>("button_y_SNES"); mCustomButtons.button_y_SNES = elem->get<std::string>(PREFIX "button_y_SNES");
if (elem->has("button_start_SNES")) if (elem->has(PREFIX "button_start_SNES"))
mCustomButtons.button_start_SNES = elem->get<std::string>("button_start_SNES"); mCustomButtons.button_start_SNES = elem->get<std::string>(PREFIX "button_start_SNES");
if (elem->has("button_back_SNES")) if (elem->has(PREFIX "button_back_SNES"))
mCustomButtons.button_back_SNES = elem->get<std::string>("button_back_SNES"); mCustomButtons.button_back_SNES = elem->get<std::string>(PREFIX "button_back_SNES");
// PS. // PlayStation.
if (elem->has("button_a_PS")) if (elem->has(PREFIX "button_a_PS"))
mCustomButtons.button_a_PS = elem->get<std::string>("button_a_PS"); mCustomButtons.button_a_PS = elem->get<std::string>(PREFIX "button_a_PS");
if (elem->has("button_b_PS")) if (elem->has(PREFIX "button_b_PS"))
mCustomButtons.button_b_PS = elem->get<std::string>("button_b_PS"); mCustomButtons.button_b_PS = elem->get<std::string>(PREFIX "button_b_PS");
if (elem->has("button_x_PS")) if (elem->has(PREFIX "button_x_PS"))
mCustomButtons.button_x_PS = elem->get<std::string>("button_x_PS"); mCustomButtons.button_x_PS = elem->get<std::string>(PREFIX "button_x_PS");
if (elem->has("button_y_PS")) if (elem->has(PREFIX "button_y_PS"))
mCustomButtons.button_y_PS = elem->get<std::string>("button_y_PS"); mCustomButtons.button_y_PS = elem->get<std::string>(PREFIX "button_y_PS");
if (elem->has("button_start_PS4")) if (elem->has(PREFIX "button_start_PS4"))
mCustomButtons.button_start_PS4 = elem->get<std::string>("button_start_PS4"); mCustomButtons.button_start_PS4 = elem->get<std::string>(PREFIX "button_start_PS4");
if (elem->has("button_back_PS4")) if (elem->has(PREFIX "button_back_PS4"))
mCustomButtons.button_back_PS4 = elem->get<std::string>("button_back_PS4"); mCustomButtons.button_back_PS4 = elem->get<std::string>(PREFIX "button_back_PS4");
if (elem->has("button_start_PS5")) if (elem->has(PREFIX "button_start_PS5"))
mCustomButtons.button_start_PS5 = elem->get<std::string>("button_start_PS5"); mCustomButtons.button_start_PS5 = elem->get<std::string>(PREFIX "button_start_PS5");
if (elem->has("button_back_PS5")) if (elem->has(PREFIX "button_back_PS5"))
mCustomButtons.button_back_PS5 = elem->get<std::string>("button_back_PS5"); mCustomButtons.button_back_PS5 = elem->get<std::string>(PREFIX "button_back_PS5");
// XBOX. // XBOX.
if (elem->has("button_a_XBOX")) if (elem->has(PREFIX "button_a_XBOX"))
mCustomButtons.button_a_XBOX = elem->get<std::string>("button_a_XBOX"); mCustomButtons.button_a_XBOX = elem->get<std::string>(PREFIX "button_a_XBOX");
if (elem->has("button_b_XBOX")) if (elem->has(PREFIX "button_b_XBOX"))
mCustomButtons.button_b_XBOX = elem->get<std::string>("button_b_XBOX"); mCustomButtons.button_b_XBOX = elem->get<std::string>(PREFIX "button_b_XBOX");
if (elem->has("button_x_XBOX")) if (elem->has(PREFIX "button_x_XBOX"))
mCustomButtons.button_x_XBOX = elem->get<std::string>("button_x_XBOX"); mCustomButtons.button_x_XBOX = elem->get<std::string>(PREFIX "button_x_XBOX");
if (elem->has("button_y_XBOX")) if (elem->has(PREFIX "button_y_XBOX"))
mCustomButtons.button_y_XBOX = elem->get<std::string>("button_y_XBOX"); mCustomButtons.button_y_XBOX = elem->get<std::string>(PREFIX "button_y_XBOX");
if (elem->has("button_start_XBOX")) if (elem->has(PREFIX "button_start_XBOX"))
mCustomButtons.button_start_XBOX = elem->get<std::string>("button_start_XBOX"); mCustomButtons.button_start_XBOX = elem->get<std::string>(PREFIX "button_start_XBOX");
if (elem->has("button_back_XBOX")) if (elem->has(PREFIX "button_back_XBOX"))
mCustomButtons.button_back_XBOX = elem->get<std::string>("button_back_XBOX"); mCustomButtons.button_back_XBOX = elem->get<std::string>(PREFIX "button_back_XBOX");
if (elem->has("button_start_XBOX360")) if (elem->has(PREFIX "button_start_XBOX360"))
mCustomButtons.button_start_XBOX360 = elem->get<std::string>("button_start_XBOX360"); mCustomButtons.button_start_XBOX360 = elem->get<std::string>(PREFIX "button_start_XBOX360");
if (elem->has("button_back_XBOX360")) if (elem->has(PREFIX "button_back_XBOX360"))
mCustomButtons.button_back_XBOX360 = elem->get<std::string>("button_back_XBOX360"); mCustomButtons.button_back_XBOX360 = elem->get<std::string>(PREFIX "button_back_XBOX360");
} }

View file

@ -133,6 +133,8 @@ void Settings::setDefaults()
mStringMap["GamelistViewStyle"] = {"automatic", "automatic"}; mStringMap["GamelistViewStyle"] = {"automatic", "automatic"};
mStringMap["TransitionStyle"] = {"slide", "slide"}; mStringMap["TransitionStyle"] = {"slide", "slide"};
mStringMap["ThemeSet"] = {"rbsimple-DE", "rbsimple-DE"}; mStringMap["ThemeSet"] = {"rbsimple-DE", "rbsimple-DE"};
mStringMap["ThemeVariant"] = {"", ""};
mStringMap["ThemeAspectRatio"] = {"", ""};
mStringMap["UIMode"] = {"full", "full"}; mStringMap["UIMode"] = {"full", "full"};
mStringMap["DefaultSortOrder"] = {"filename, ascending", "filename, ascending"}; mStringMap["DefaultSortOrder"] = {"filename, ascending", "filename, ascending"};
mStringMap["MenuOpeningEffect"] = {"scale-up", "scale-up"}; mStringMap["MenuOpeningEffect"] = {"scale-up", "scale-up"};

View file

@ -33,18 +33,20 @@ std::shared_ptr<Sound> Sound::getFromTheme(ThemeData* const theme,
const std::string& view, const std::string& view,
const std::string& element) const std::string& element)
{ {
std::string elemName {element.substr(6, std::string::npos)};
if (theme == nullptr) { if (theme == nullptr) {
LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" << element LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" << elemName
<< "\""; << "\"";
return get(ResourceManager::getInstance().getResourcePath(":/sounds/" + element + ".wav")); return get(ResourceManager::getInstance().getResourcePath(":/sounds/" + elemName + ".wav"));
} }
LOG(LogDebug) << "Sound::getFromTheme(): Looking for tag <sound name=\"" << element << "\">"; LOG(LogDebug) << "Sound::getFromTheme(): Looking for tag <sound name=\"" << elemName << "\">";
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound"); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound");
if (!elem || !elem->has("path")) { if (!elem || !elem->has("path")) {
LOG(LogDebug) << "Sound::getFromTheme(): Tag not found, using fallback sound file"; LOG(LogDebug) << "Sound::getFromTheme(): Tag not found, using fallback sound file";
return get(ResourceManager::getInstance().getResourcePath(":/sounds/" + element + ".wav")); return get(ResourceManager::getInstance().getResourcePath(":/sounds/" + elemName + ".wav"));
} }
LOG(LogDebug) << "Sound::getFromTheme(): Tag found, ready to load theme sound file"; LOG(LogDebug) << "Sound::getFromTheme(): Tag found, ready to load theme sound file";
@ -79,8 +81,7 @@ void Sound::init()
Uint8* data = nullptr; Uint8* data = nullptr;
Uint32 dlen = 0; Uint32 dlen = 0;
if (SDL_LoadWAV(mPath.c_str(), &wave, &data, &dlen) == nullptr) { if (SDL_LoadWAV(mPath.c_str(), &wave, &data, &dlen) == nullptr) {
LOG(LogError) << "Failed to load theme navigation sound file:"; LOG(LogError) << "Failed to load theme navigation sound file: " << SDL_GetError();
LOG(LogError) << SDL_GetError();
return; return;
} }
@ -90,14 +91,12 @@ void Sound::init()
AudioManager::sAudioFormat.channels, AudioManager::sAudioFormat.freq); AudioManager::sAudioFormat.channels, AudioManager::sAudioFormat.freq);
if (conversionStream == nullptr) { if (conversionStream == nullptr) {
LOG(LogError) << "Failed to create sample conversion stream:"; LOG(LogError) << "Failed to create sample conversion stream: " << SDL_GetError();
LOG(LogError) << SDL_GetError();
return; return;
} }
if (SDL_AudioStreamPut(conversionStream, data, dlen) == -1) { if (SDL_AudioStreamPut(conversionStream, data, dlen) == -1) {
LOG(LogError) << "Failed to put samples in the conversion stream:"; LOG(LogError) << "Failed to put samples in the conversion stream: " << SDL_GetError();
LOG(LogError) << SDL_GetError();
SDL_FreeAudioStream(conversionStream); SDL_FreeAudioStream(conversionStream);
return; return;
} }
@ -106,8 +105,7 @@ void Sound::init()
Uint8* converted = new Uint8[sampleLength]; Uint8* converted = new Uint8[sampleLength];
if (SDL_AudioStreamGet(conversionStream, converted, sampleLength) == -1) { if (SDL_AudioStreamGet(conversionStream, converted, sampleLength) == -1) {
LOG(LogError) << "Failed to convert sound file '" << mPath << "':"; LOG(LogError) << "Failed to convert sound file '" << mPath << "': " << SDL_GetError();
LOG(LogError) << SDL_GetError();
SDL_FreeAudioStream(conversionStream); SDL_FreeAudioStream(conversionStream);
delete[] converted; delete[] converted;
return; return;
@ -209,13 +207,13 @@ void NavigationSounds::loadThemeNavigationSounds(ThemeData* const theme)
"Theme set does not include navigation sound support, using fallback sounds"; "Theme set does not include navigation sound support, using fallback sounds";
} }
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "systembrowse")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_systembrowse"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "quicksysselect")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_quicksysselect"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "select")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_select"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "back")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_back"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "scroll")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_scroll"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "favorite")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_favorite"));
mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "launch")); mNavigationSounds.push_back(Sound::getFromTheme(theme, "all", "sound_launch"));
} }
void NavigationSounds::playThemeNavigationSound(NavigationSoundsID soundID) void NavigationSounds::playThemeNavigationSound(NavigationSoundsID soundID)

File diff suppressed because it is too large Load diff

View file

@ -3,9 +3,9 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// ThemeData.h // ThemeData.h
// //
// Finds available themes on the file system and loads these, // Finds available themes on the file system and loads and parses these.
// including the parsing of individual theme components // Basic error checking for valid elements and data types is done here,
// (includes, features, variables, views, elements). // with additional validation handled by the individual components.
// //
#ifndef ES_CORE_THEME_DATA_H #ifndef ES_CORE_THEME_DATA_H
@ -158,8 +158,31 @@ public:
ThemeData(); ThemeData();
class ThemeView
{
public:
std::map<std::string, ThemeElement> elements;
std::vector<std::string> legacyOrderedKeys;
};
struct ThemeVariant {
std::string name;
std::string label;
bool selectable;
bool override;
std::string overrideTrigger;
std::string overrideVariant;
};
struct ThemeCapability {
std::vector<ThemeVariant> variants;
std::vector<std::string> aspectRatios;
bool legacyTheme;
};
struct ThemeSet { struct ThemeSet {
std::string path; std::string path;
ThemeCapability capabilities;
std::string getName() const { return Utils::FileSystem::getStem(path); } std::string getName() const { return Utils::FileSystem::getStem(path); }
std::string getThemePath(const std::string& system) const std::string getThemePath(const std::string& system) const
@ -168,21 +191,22 @@ public:
} }
}; };
// Throws ThemeException.
void loadFile(const std::map<std::string, std::string>& sysDataMap, const std::string& path); void loadFile(const std::map<std::string, std::string>& sysDataMap, const std::string& path);
bool hasView(const std::string& view); bool hasView(const std::string& view);
ThemeView& getViewElements(std::string view) { return mViews[view]; }
static std::vector<GuiComponent*> makeExtras(const std::shared_ptr<ThemeData>& theme, static std::vector<GuiComponent*> makeExtras(const std::shared_ptr<ThemeData>& theme,
const std::string& view); const std::string& view);
// If expectedType is an empty string, then do no type checking.
const ThemeElement* getElement(const std::string& view, const ThemeElement* getElement(const std::string& view,
const std::string& element, const std::string& element,
const std::string& expectedType) const; const std::string& expectedType) const;
static std::map<std::string, ThemeSet> getThemeSets(); static std::map<std::string, ThemeSet>& getThemeSets();
static std::string getThemeFromCurrentSet(const std::string& system); static std::string getThemeFromCurrentSet(const std::string& system);
const bool isLegacyTheme() { return mLegacyTheme; }
enum ElementPropertyType { enum ElementPropertyType {
NORMALIZED_RECT, NORMALIZED_RECT,
NORMALIZED_PAIR, NORMALIZED_PAIR,
@ -196,26 +220,16 @@ public:
std::map<std::string, std::string> mVariables; std::map<std::string, std::string> mVariables;
private: private:
class ThemeView
{
public:
std::map<std::string, ThemeElement> elements;
std::vector<std::string> orderedKeys;
};
static std::map<std::string, std::map<std::string, ElementPropertyType>> sElementMap;
static std::vector<std::string> sSupportedFeatures;
static std::vector<std::string> sSupportedViews;
std::deque<std::string> mPaths;
float mVersion;
static const std::shared_ptr<ThemeData> getDefault(); static const std::shared_ptr<ThemeData> getDefault();
unsigned int getHexColor(const std::string& str); unsigned int getHexColor(const std::string& str);
std::string resolvePlaceholders(const std::string& in); std::string resolvePlaceholders(const std::string& in);
static ThemeCapability parseThemeCapabilities(const std::string& path);
void parseIncludes(const pugi::xml_node& themeRoot); void parseIncludes(const pugi::xml_node& themeRoot);
void parseFeatures(const pugi::xml_node& themeRoot); void parseFeatures(const pugi::xml_node& themeRoot);
void parseVariants(const pugi::xml_node& themeRoot);
void parseAspectRatios(const pugi::xml_node& themeRoot);
void parseVariables(const pugi::xml_node& root); void parseVariables(const pugi::xml_node& root);
void parseViews(const pugi::xml_node& themeRoot); void parseViews(const pugi::xml_node& themeRoot);
void parseView(const pugi::xml_node& viewNode, ThemeView& view); void parseView(const pugi::xml_node& viewNode, ThemeView& view);
@ -223,7 +237,24 @@ private:
const std::map<std::string, ElementPropertyType>& typeMap, const std::map<std::string, ElementPropertyType>& typeMap,
ThemeElement& element); ThemeElement& element);
static std::map<std::string, std::map<std::string, ElementPropertyType>> sElementMap;
static std::map<std::string, std::map<std::string, std::string>> sPropertyAttributeMap;
static std::vector<std::string> sLegacySupportedFeatures;
static std::vector<std::string> sLegacySupportedViews;
static std::vector<std::string> sSupportedViews;
static std::vector<std::string> sSupportedAspectRatios;
static inline std::map<std::string, ThemeSet> mThemeSets;
std::map<std::string, ThemeData::ThemeSet>::iterator mCurrentThemeSet;
std::map<std::string, ThemeView> mViews; std::map<std::string, ThemeView> mViews;
std::deque<std::string> mPaths;
std::vector<std::string> mVariants;
std::vector<std::string> mAspectRatios;
std::string mSelectedVariant;
std::string mSelectedAspectRatio;
bool mLegacyTheme;
}; };
#endif // ES_CORE_THEME_DATA_H #endif // ES_CORE_THEME_DATA_H

View file

@ -290,8 +290,10 @@ void BadgeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
for (auto slot : slots) { for (auto slot : slots) {
if (std::find(mBadgeTypes.cbegin(), mBadgeTypes.cend(), slot) != mBadgeTypes.end()) { if (std::find(mBadgeTypes.cbegin(), mBadgeTypes.cend(), slot) != mBadgeTypes.end()) {
if (properties & PATH && elem->has(slot)) // The "badge_" string is required as ThemeData adds this as a prefix to avoid
mBadgeIcons[slot] = elem->get<std::string>(slot); // name collisions when using XML attributes.
if (properties & PATH && elem->has("badge_" + slot))
mBadgeIcons[slot] = elem->get<std::string>("badge_" + slot);
FlexboxComponent::FlexboxItem item; FlexboxComponent::FlexboxItem item;
item.label = slot; item.label = slot;
@ -309,8 +311,9 @@ void BadgeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
} }
for (auto& gameController : sGameControllers) { for (auto& gameController : sGameControllers) {
if (properties & PATH && elem->has(gameController.shortName)) if (properties & PATH && elem->has("controller_" + gameController.shortName))
gameController.fileName = elem->get<std::string>(gameController.shortName); gameController.fileName =
elem->get<std::string>("controller_" + gameController.shortName);
} }
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);

View file

@ -51,7 +51,7 @@ public:
unsigned int properties) override; unsigned int properties) override;
private: private:
inline static std::vector<GameControllers> sGameControllers; static inline std::vector<GameControllers> sGameControllers;
std::vector<FlexboxComponent::FlexboxItem> mFlexboxItems; std::vector<FlexboxComponent::FlexboxItem> mFlexboxItems;
FlexboxComponent mFlexboxComponent; FlexboxComponent mFlexboxComponent;

View file

@ -111,7 +111,6 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);
using namespace ThemeFlags; using namespace ThemeFlags;
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime"); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");

View file

@ -475,7 +475,6 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
using namespace ThemeFlags; using namespace ThemeFlags;
GuiComponent::applyTheme(theme, view, element, GuiComponent::applyTheme(theme, view, element,
(properties ^ ThemeFlags::SIZE) | (properties ^ ThemeFlags::SIZE) |
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
@ -505,6 +504,9 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
setImage(elem->get<std::string>("path"), tile); setImage(elem->get<std::string>("path"), tile);
} }
if (properties & METADATA && elem->has("metadata"))
setMetadataField(elem->get<std::string>("metadata"));
if (properties & COLOR) { if (properties & COLOR) {
if (elem->has("color")) if (elem->has("color"))
setColorShift(elem->get<unsigned int>("color")); setColorShift(elem->get<unsigned int>("color"));

View file

@ -214,7 +214,6 @@ void LottieComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
using namespace ThemeFlags; using namespace ThemeFlags;
const ThemeData::ThemeElement* elem {theme->getElement(view, element, "animation")}; const ThemeData::ThemeElement* elem {theme->getElement(view, element, "animation")};
if (elem->has("size")) { if (elem->has("size")) {

View file

@ -50,8 +50,8 @@ private:
std::vector<uint8_t> mPictureRGBA; std::vector<uint8_t> mPictureRGBA;
std::unordered_map<size_t, std::vector<uint8_t>> mFrameCache; std::unordered_map<size_t, std::vector<uint8_t>> mFrameCache;
// Set a 1024 MiB total Lottie animation cache as default. // Set a 1024 MiB total Lottie animation cache as default.
inline static size_t mMaxTotalFrameCache = 1024 * 1024 * 1024; static inline size_t mMaxTotalFrameCache = 1024 * 1024 * 1024;
inline static size_t mTotalFrameCache; static inline size_t mTotalFrameCache;
bool mCacheFrames; bool mCacheFrames;
size_t mMaxCacheSize; size_t mMaxCacheSize;
size_t mCacheSize; size_t mCacheSize;

View file

@ -211,7 +211,6 @@ void RatingComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
using namespace ThemeFlags; using namespace ThemeFlags;
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "rating"); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "rating");
if (!elem) if (!elem)
return; return;

View file

@ -291,11 +291,15 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& element, const std::string& element,
unsigned int properties) unsigned int properties)
{ {
using namespace ThemeFlags;
GuiComponent::applyTheme(theme, view, element, properties); GuiComponent::applyTheme(theme, view, element, properties);
using namespace ThemeFlags; std::string elementType {"text"};
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text"); if (element.substr(0, 13) == "gamelistinfo_")
elementType = "gamelistinfo";
const ThemeData::ThemeElement* elem = theme->getElement(view, element, elementType);
if (!elem) if (!elem)
return; return;

View file

@ -75,13 +75,13 @@ bool VideoComponent::setVideo(std::string path)
return false; return false;
} }
void VideoComponent::setImage(std::string path) void VideoComponent::setImage(const std::string& path, bool tile, bool linearMagnify)
{ {
// Check that the image has changed. // Check that the image has changed.
if (path == mStaticImagePath) if (path == mStaticImagePath)
return; return;
mStaticImage.setImage(path); mStaticImage.setImage(path, tile, linearMagnify);
mStaticImagePath = path; mStaticImagePath = path;
} }
@ -212,7 +212,6 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
unsigned int properties) unsigned int properties)
{ {
using namespace ThemeFlags; using namespace ThemeFlags;
GuiComponent::applyTheme(theme, view, element, GuiComponent::applyTheme(theme, view, element,
(properties ^ ThemeFlags::SIZE) | (properties ^ ThemeFlags::SIZE) |
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0)); ((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
@ -245,14 +244,24 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("default")) if (elem->has("default"))
mConfig.defaultVideoPath = elem->get<std::string>("default"); mConfig.defaultVideoPath = elem->get<std::string>("default");
if (elem->has("path"))
mConfig.staticVideoPath = elem->get<std::string>("path");
if ((properties & ThemeFlags::DELAY) && elem->has("delay")) if ((properties & ThemeFlags::DELAY) && elem->has("delay"))
mConfig.startDelay = static_cast<unsigned>((elem->get<float>("delay") * 1000.0f)); mConfig.startDelay = static_cast<unsigned>((elem->get<float>("delay") * 1000.0f));
if (elem->has("showSnapshotNoVideo")) if (!theme->isLegacyTheme())
mConfig.showSnapshotNoVideo = true;
else if (elem->has("showSnapshotNoVideo"))
mConfig.showSnapshotNoVideo = elem->get<bool>("showSnapshotNoVideo"); mConfig.showSnapshotNoVideo = elem->get<bool>("showSnapshotNoVideo");
if (elem->has("showSnapshotDelay")) if (!theme->isLegacyTheme() && mConfig.startDelay != 0)
mConfig.showSnapshotDelay = true;
else if (elem->has("showSnapshotDelay"))
mConfig.showSnapshotDelay = elem->get<bool>("showSnapshotDelay"); mConfig.showSnapshotDelay = elem->get<bool>("showSnapshotDelay");
if (properties & METADATA && elem->has("imageMetadata"))
setMetadataField(elem->get<std::string>("imageMetadata"));
} }
std::vector<HelpPrompt> VideoComponent::getHelpPrompts() std::vector<HelpPrompt> VideoComponent::getHelpPrompts()

View file

@ -26,6 +26,7 @@ class VideoComponent : public GuiComponent
bool showSnapshotNoVideo; bool showSnapshotNoVideo;
bool showSnapshotDelay; bool showSnapshotDelay;
std::string defaultVideoPath; std::string defaultVideoPath;
std::string staticVideoPath;
}; };
public: public:
@ -36,8 +37,10 @@ public:
bool setVideo(std::string path); bool setVideo(std::string path);
// Configures the component to show the default video. // Configures the component to show the default video.
void setDefaultVideo() { setVideo(mConfig.defaultVideoPath); } void setDefaultVideo() { setVideo(mConfig.defaultVideoPath); }
// Configures the component to show the static video.
void setStaticVideo() { setVideo(mConfig.staticVideoPath); }
// Loads a static image that is displayed if the video cannot be played. // Loads a static image that is displayed if the video cannot be played.
void setImage(std::string path); void setImage(const std::string& path, bool tile = false, bool linearMagnify = false);
// Sets whether we're in media viewer mode. // Sets whether we're in media viewer mode.
void setMediaViewerMode(bool isMediaViewer) { mMediaViewerMode = isMediaViewer; } void setMediaViewerMode(bool isMediaViewer) { mMediaViewerMode = isMediaViewer; }
// Sets whether we're in screensaver mode. // Sets whether we're in screensaver mode.
@ -45,6 +48,8 @@ public:
// Set the opacity for the embedded static image. // Set the opacity for the embedded static image.
void setOpacity(unsigned char opacity) override { mOpacity = opacity; } void setOpacity(unsigned char opacity) override { mOpacity = opacity; }
bool hasStaticVideo() { return !mConfig.staticVideoPath.empty(); }
void onShow() override; void onShow() override;
void onHide() override; void onHide() override;
void onStopVideo() override; void onStopVideo() override;

View file

@ -215,6 +215,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
Renderer::drawTriangleStrips(&vertices[0], 4, trans); Renderer::drawTriangleStrips(&vertices[0], 4, trans);
} }
else { else {
if (mVisible)
VideoComponent::renderSnapshot(parentTrans); VideoComponent::renderSnapshot(parentTrans);
} }
} }