2020-09-15 20:57:54 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-06 14:48:05 +00:00
|
|
|
//
|
2024-07-10 16:04:40 +00:00
|
|
|
// ES-DE Frontend
|
2020-06-21 12:25:28 +00:00
|
|
|
// SystemView.cpp
|
2020-06-06 14:48:05 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// Main system view.
|
2020-06-06 14:48:05 +00:00
|
|
|
//
|
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "views/SystemView.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
|
|
|
|
#include "Log.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "Settings.h"
|
2020-06-17 20:13:07 +00:00
|
|
|
#include "Sound.h"
|
2022-01-17 20:53:23 +00:00
|
|
|
#include "UIModeController.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "Window.h"
|
2021-07-07 18:03:42 +00:00
|
|
|
#include "animations/LambdaAnimation.h"
|
|
|
|
|
#include "guis/GuiMsgBox.h"
|
2024-07-13 17:14:34 +00:00
|
|
|
#include "utils/LocalizationUtil.h"
|
2021-07-07 18:03:42 +00:00
|
|
|
#include "views/ViewController.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-08-23 19:27:01 +00:00
|
|
|
#if defined(_WIN64)
|
|
|
|
|
#include <cmath>
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
SystemView::SystemView()
|
2022-03-14 18:51:48 +00:00
|
|
|
: mRenderer {Renderer::getInstance()}
|
2022-03-24 22:05:23 +00:00
|
|
|
, mPrimary {nullptr}
|
|
|
|
|
, mPrimaryType {PrimaryType::CAROUSEL}
|
2022-09-20 19:16:39 +00:00
|
|
|
, mLastCursor {-1}
|
2022-03-14 18:51:48 +00:00
|
|
|
, mCamOffset {0.0f}
|
2022-02-06 13:01:40 +00:00
|
|
|
, mFadeOpacity {0.0f}
|
2022-02-08 23:05:06 +00:00
|
|
|
, mPreviousScrollVelocity {0}
|
2022-01-16 17:18:28 +00:00
|
|
|
, mUpdatedGameCount {false}
|
|
|
|
|
, mViewNeedsReload {true}
|
2022-02-19 20:22:46 +00:00
|
|
|
, mNavigated {false}
|
2022-03-11 22:20:27 +00:00
|
|
|
, mMaxFade {false}
|
|
|
|
|
, mFadeTransitions {false}
|
2022-09-23 20:47:49 +00:00
|
|
|
, mTransitionAnim {false}
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2022-02-11 22:38:23 +00:00
|
|
|
setSize(Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
2022-02-06 19:13:53 +00:00
|
|
|
populate();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2022-09-23 15:19:24 +00:00
|
|
|
void SystemView::onShow()
|
|
|
|
|
{
|
2022-09-24 14:50:14 +00:00
|
|
|
finishAnimation(0);
|
2023-02-21 17:47:05 +00:00
|
|
|
stopViewVideos();
|
2022-09-24 14:50:14 +00:00
|
|
|
mFadeOpacity = 0.0f;
|
|
|
|
|
mTransitionAnim = false;
|
2023-08-09 16:57:23 +00:00
|
|
|
mPrimary->onShowPrimary();
|
2022-09-23 15:19:24 +00:00
|
|
|
}
|
|
|
|
|
|
2024-09-15 09:34:28 +00:00
|
|
|
void SystemView::onHide()
|
|
|
|
|
{
|
|
|
|
|
for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents)
|
|
|
|
|
video->stopVideoPlayer(false);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 21:46:52 +00:00
|
|
|
void SystemView::onTransition()
|
|
|
|
|
{
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
2022-02-19 21:46:52 +00:00
|
|
|
anim->setPauseAnimation(true);
|
2022-03-05 20:10:40 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
2022-03-05 20:10:40 +00:00
|
|
|
anim->setPauseAnimation(true);
|
2022-09-23 20:47:49 +00:00
|
|
|
|
|
|
|
|
if (mFadeTransitions)
|
|
|
|
|
mTransitionAnim = true;
|
2022-02-19 21:46:52 +00:00
|
|
|
}
|
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void SystemView::goToSystem(SystemData* system, bool animate)
|
|
|
|
|
{
|
2022-03-24 22:05:23 +00:00
|
|
|
mPrimary->setCursor(system);
|
2022-02-14 18:32:07 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& selector : mSystemElements[mPrimary->getCursor()].gameSelectors) {
|
2022-02-14 18:32:07 +00:00
|
|
|
if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM)
|
|
|
|
|
selector->setNeedsRefresh();
|
|
|
|
|
}
|
|
|
|
|
|
2023-08-08 17:18:16 +00:00
|
|
|
// Reset horizontally scrolling text.
|
2024-09-01 09:30:16 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].gameCountComponents)
|
|
|
|
|
text->resetComponent();
|
2023-08-07 20:58:35 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].textComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
text->resetComponent();
|
2023-08-07 20:58:35 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents)
|
2022-02-19 20:22:46 +00:00
|
|
|
video->setStaticVideo();
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
anim->resetComponent();
|
2022-02-19 21:46:52 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
anim->resetComponent();
|
2022-03-05 20:10:40 +00:00
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
updateGameSelectors();
|
2020-11-15 19:06:33 +00:00
|
|
|
updateGameCount();
|
2022-02-19 20:22:46 +00:00
|
|
|
startViewVideos();
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (!animate)
|
2022-02-06 13:01:40 +00:00
|
|
|
finishSystemAnimation(0);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool SystemView::input(InputConfig* config, Input input)
|
|
|
|
|
{
|
2022-02-19 20:22:46 +00:00
|
|
|
mNavigated = false;
|
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (input.value != 0) {
|
|
|
|
|
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r &&
|
2021-07-07 18:03:42 +00:00
|
|
|
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
2021-03-21 10:26:28 +00:00
|
|
|
LOG(LogDebug) << "SystemView::input(): Reloading all";
|
2022-08-14 19:31:59 +00:00
|
|
|
TextureResource::manualUnloadAll();
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->reloadAll();
|
2020-06-21 12:25:28 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (config->isMappedTo("a", input)) {
|
2022-03-24 22:05:23 +00:00
|
|
|
mPrimary->stopScrolling();
|
2022-02-19 20:22:46 +00:00
|
|
|
pauseViewVideos();
|
2022-03-24 22:05:23 +00:00
|
|
|
ViewController::getInstance()->goToGamelist(mPrimary->getSelected());
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND);
|
2020-06-21 12:25:28 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2023-03-06 18:27:59 +00:00
|
|
|
if (Settings::getInstance()->getString("RandomEntryButton") == "gamessystems" &&
|
2021-07-07 18:03:42 +00:00
|
|
|
(config->isMappedTo("leftthumbstickclick", input) ||
|
|
|
|
|
config->isMappedTo("rightthumbstickclick", input))) {
|
2021-05-16 12:03:13 +00:00
|
|
|
// Get a random system and jump to it.
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SYSTEMBROWSESOUND);
|
2022-09-23 15:19:24 +00:00
|
|
|
mPrimary->stopScrolling();
|
2023-03-06 18:27:59 +00:00
|
|
|
ViewController::getInstance()->cancelViewTransitions();
|
2022-03-24 22:05:23 +00:00
|
|
|
mPrimary->setCursor(SystemData::getRandomSystem(mPrimary->getSelected()));
|
2020-06-21 12:25:28 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-09-18 16:40:22 +00:00
|
|
|
|
2022-12-05 20:21:48 +00:00
|
|
|
if (config->isMappedTo("x", input) &&
|
2021-07-07 18:03:42 +00:00
|
|
|
Settings::getInstance()->getBool("ScreensaverControls")) {
|
2020-11-10 21:18:20 +00:00
|
|
|
if (!mWindow->isScreensaverActive()) {
|
2022-01-04 20:49:22 +00:00
|
|
|
ViewController::getInstance()->stopScrolling();
|
|
|
|
|
ViewController::getInstance()->cancelViewTransitions();
|
2022-05-18 21:56:51 +00:00
|
|
|
mWindow->startScreensaver(false);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
return mPrimary->input(config, input);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SystemView::update(int deltaTime)
|
|
|
|
|
{
|
2022-03-24 22:05:23 +00:00
|
|
|
mPrimary->update(deltaTime);
|
2022-02-19 20:22:46 +00:00
|
|
|
|
2024-09-01 09:30:16 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].gameCountComponents)
|
|
|
|
|
text->update(deltaTime);
|
|
|
|
|
|
2023-08-07 20:58:35 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].textComponents)
|
|
|
|
|
text->update(deltaTime);
|
|
|
|
|
|
2022-09-20 19:16:39 +00:00
|
|
|
for (auto& video : mSystemElements[mPrimary->getCursor()].videoComponents) {
|
|
|
|
|
if (!isScrolling())
|
|
|
|
|
video->update(deltaTime);
|
|
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
2022-02-19 21:46:52 +00:00
|
|
|
anim->update(deltaTime);
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
2022-03-05 20:10:40 +00:00
|
|
|
anim->update(deltaTime);
|
|
|
|
|
|
2023-03-26 17:38:30 +00:00
|
|
|
for (auto& container : mSystemElements[mPrimary->getCursor()].containerComponents)
|
|
|
|
|
container->update(deltaTime);
|
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
GuiComponent::update(deltaTime);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
void SystemView::render(const glm::mat4& parentTrans)
|
|
|
|
|
{
|
2022-03-28 15:47:34 +00:00
|
|
|
if (mPrimary == nullptr)
|
2022-02-06 13:55:48 +00:00
|
|
|
return; // Nothing to render.
|
|
|
|
|
|
2022-09-26 18:02:31 +00:00
|
|
|
bool transitionFade {false};
|
2022-02-19 20:22:46 +00:00
|
|
|
|
2022-03-11 22:20:27 +00:00
|
|
|
if (mNavigated && mMaxFade)
|
2022-09-26 18:02:31 +00:00
|
|
|
transitionFade = true;
|
2022-02-19 20:22:46 +00:00
|
|
|
|
2022-09-26 18:02:31 +00:00
|
|
|
if (!transitionFade)
|
2022-02-19 20:22:46 +00:00
|
|
|
renderElements(parentTrans, false);
|
2022-02-06 13:55:48 +00:00
|
|
|
glm::mat4 trans {getTransform() * parentTrans};
|
|
|
|
|
|
2022-09-25 20:57:43 +00:00
|
|
|
// Make sure that the primary component doesn't render outside our designated area.
|
2022-09-12 17:06:09 +00:00
|
|
|
mRenderer->pushClipRect(
|
|
|
|
|
glm::ivec2 {static_cast<int>(std::round(trans[3].x)),
|
|
|
|
|
static_cast<int>(std::round(trans[3].y))},
|
|
|
|
|
glm::ivec2 {static_cast<int>(std::round(mSize.x)), static_cast<int>(std::round(mSize.y))});
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
mPrimary->render(trans);
|
2022-09-25 18:55:26 +00:00
|
|
|
mRenderer->popClipRect();
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2022-09-26 18:02:31 +00:00
|
|
|
if (!mPrimary->getFadeAbovePrimary() || !transitionFade)
|
2022-02-09 17:22:06 +00:00
|
|
|
renderElements(parentTrans, true);
|
2022-02-06 13:55:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SystemView::onThemeChanged(const std::shared_ptr<ThemeData>& /*theme*/)
|
|
|
|
|
{
|
|
|
|
|
LOG(LogDebug) << "SystemView::onThemeChanged()";
|
|
|
|
|
mViewNeedsReload = true;
|
|
|
|
|
populate();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> SystemView::getHelpPrompts()
|
|
|
|
|
{
|
|
|
|
|
std::vector<HelpPrompt> prompts;
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mCarousel != nullptr) {
|
|
|
|
|
if (mCarousel->getType() == CarouselComponent<SystemData*>::CarouselType::VERTICAL ||
|
|
|
|
|
mCarousel->getType() == CarouselComponent<SystemData*>::CarouselType::VERTICAL_WHEEL)
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("up/down", _("choose")));
|
2022-03-24 22:05:23 +00:00
|
|
|
else
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("left/right", _("choose")));
|
2022-03-24 22:05:23 +00:00
|
|
|
}
|
2022-12-04 14:56:59 +00:00
|
|
|
else if (mGrid != nullptr) {
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("up/down/left/right", _("choose")));
|
2022-12-04 14:56:59 +00:00
|
|
|
}
|
2022-03-24 22:05:23 +00:00
|
|
|
else if (mTextList != nullptr) {
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("up/down", _("choose")));
|
2022-03-24 22:05:23 +00:00
|
|
|
}
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2024-07-13 17:14:34 +00:00
|
|
|
prompts.push_back(HelpPrompt("a", _("select")));
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2023-03-06 18:27:59 +00:00
|
|
|
if (Settings::getInstance()->getString("RandomEntryButton") == "gamessystems")
|
2024-07-13 20:14:37 +00:00
|
|
|
prompts.push_back(HelpPrompt("thumbstickclick", _("random")));
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2022-05-16 17:55:08 +00:00
|
|
|
if (Settings::getInstance()->getBool("ScreensaverControls"))
|
2024-07-13 20:14:37 +00:00
|
|
|
prompts.push_back(HelpPrompt("x", _("screensaver")));
|
2022-02-06 13:55:48 +00:00
|
|
|
|
|
|
|
|
return prompts;
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
void SystemView::onCursorChanged(const CursorState& state)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2023-08-08 17:18:16 +00:00
|
|
|
// Reset horizontally scrolling text.
|
2024-09-01 09:30:16 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].gameCountComponents)
|
|
|
|
|
text->resetComponent();
|
2023-08-07 20:58:35 +00:00
|
|
|
for (auto& text : mSystemElements[mPrimary->getCursor()].textComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
text->resetComponent();
|
2023-08-07 20:58:35 +00:00
|
|
|
|
2022-11-25 17:57:06 +00:00
|
|
|
const int cursor {mPrimary->getCursor()};
|
2022-12-04 14:56:59 +00:00
|
|
|
const int scrollVelocity {mPrimary->getScrollingVelocity()};
|
2023-01-08 16:00:36 +00:00
|
|
|
const ViewTransitionAnimation transitionAnim {static_cast<ViewTransitionAnimation>(
|
|
|
|
|
Settings::getInstance()->getInt("TransitionsSystemToSystem"))};
|
|
|
|
|
mFadeTransitions = (transitionAnim == ViewTransitionAnimation::FADE);
|
2022-02-14 18:32:07 +00:00
|
|
|
|
2022-12-04 14:56:59 +00:00
|
|
|
// Some logic needed to avoid various navigation glitches with GridComponent and
|
|
|
|
|
// TextListComponent.
|
|
|
|
|
if (state == CursorState::CURSOR_STOPPED && mCarousel == nullptr) {
|
|
|
|
|
const int numEntries {static_cast<int>(mPrimary->getNumEntries())};
|
|
|
|
|
bool doStop {false};
|
|
|
|
|
|
|
|
|
|
if (cursor == 0 && mLastCursor == numEntries - 1 && std::abs(scrollVelocity) == 1)
|
|
|
|
|
doStop = false;
|
|
|
|
|
else if (cursor == 0)
|
|
|
|
|
doStop = true;
|
|
|
|
|
else if (cursor == numEntries - 1 && mLastCursor == 0 && std::abs(scrollVelocity) == 1)
|
|
|
|
|
doStop = false;
|
|
|
|
|
else if (cursor == numEntries - 1)
|
|
|
|
|
doStop = true;
|
|
|
|
|
|
|
|
|
|
if (!doStop && mGrid != nullptr && std::abs(scrollVelocity) == mGrid->getColumnCount()) {
|
|
|
|
|
const int columns {mGrid->getColumnCount()};
|
|
|
|
|
const int columnModulus {numEntries % columns};
|
|
|
|
|
|
|
|
|
|
if (cursor < columns)
|
|
|
|
|
doStop = true;
|
|
|
|
|
else if (cursor >= numEntries - (columnModulus == 0 ? columns : columnModulus))
|
|
|
|
|
doStop = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (doStop) {
|
|
|
|
|
if (mGrid != nullptr)
|
|
|
|
|
mGrid->setScrollVelocity(0);
|
|
|
|
|
mPrimary->stopScrolling();
|
|
|
|
|
mNavigated = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-20 19:16:39 +00:00
|
|
|
// Avoid double updates.
|
2022-09-23 15:19:24 +00:00
|
|
|
if (cursor != mLastCursor) {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
|
|
|
|
if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM)
|
|
|
|
|
selector->setNeedsRefresh();
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-20 19:16:39 +00:00
|
|
|
|
2022-11-03 16:11:43 +00:00
|
|
|
if (mLastCursor >= 0 && mLastCursor <= static_cast<int>(mSystemElements.size())) {
|
2024-09-15 09:34:28 +00:00
|
|
|
if (transitionAnim == ViewTransitionAnimation::INSTANT || isAnimationPlaying(0)) {
|
|
|
|
|
for (auto& video : mSystemElements[mLastCursor].videoComponents)
|
|
|
|
|
video->stopVideoPlayer(false);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& video : mSystemElements[mLastCursor].videoComponents)
|
|
|
|
|
video->pauseVideoPlayer();
|
|
|
|
|
}
|
2022-11-03 16:11:43 +00:00
|
|
|
}
|
|
|
|
|
|
2023-03-26 17:38:30 +00:00
|
|
|
for (auto& container : mSystemElements[mPrimary->getCursor()].containerComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
container->resetComponent();
|
2023-03-26 17:38:30 +00:00
|
|
|
|
2022-11-25 17:57:06 +00:00
|
|
|
// This is needed to avoid erratic camera movements during extreme navigation input when using
|
|
|
|
|
// slide transitions. This should very rarely occur during normal application usage.
|
2023-01-08 16:00:36 +00:00
|
|
|
if (transitionAnim == ViewTransitionAnimation::SLIDE) {
|
2022-11-25 17:57:06 +00:00
|
|
|
bool resetCamOffset {false};
|
|
|
|
|
|
|
|
|
|
if (scrollVelocity == -1 && mPreviousScrollVelocity == 1) {
|
|
|
|
|
if (mLastCursor > cursor && mCamOffset > static_cast<float>(mLastCursor))
|
|
|
|
|
resetCamOffset = true;
|
|
|
|
|
else if (mLastCursor > cursor && mCamOffset < static_cast<float>(cursor))
|
|
|
|
|
resetCamOffset = true;
|
|
|
|
|
else if (mLastCursor < cursor && mCamOffset <= static_cast<float>(cursor) &&
|
|
|
|
|
mCamOffset != static_cast<float>(mLastCursor))
|
|
|
|
|
resetCamOffset = true;
|
|
|
|
|
}
|
|
|
|
|
else if (scrollVelocity == 1 && mPreviousScrollVelocity == -1) {
|
|
|
|
|
if (mLastCursor > cursor && mCamOffset < static_cast<float>(mLastCursor))
|
|
|
|
|
resetCamOffset = true;
|
|
|
|
|
else if (mLastCursor < cursor && mCamOffset > static_cast<float>(cursor))
|
|
|
|
|
resetCamOffset = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (resetCamOffset)
|
|
|
|
|
mCamOffset = static_cast<float>(cursor);
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-15 09:34:28 +00:00
|
|
|
const int prevLastCursor {mLastCursor};
|
2022-09-20 19:16:39 +00:00
|
|
|
mLastCursor = cursor;
|
|
|
|
|
|
2022-02-19 20:22:46 +00:00
|
|
|
for (auto& video : mSystemElements[cursor].videoComponents)
|
|
|
|
|
video->setStaticVideo();
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
anim->resetComponent();
|
2022-02-19 21:46:52 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
2023-08-08 17:18:16 +00:00
|
|
|
anim->resetComponent();
|
2022-03-05 20:10:40 +00:00
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
updateGameSelectors();
|
2022-02-19 20:22:46 +00:00
|
|
|
startViewVideos();
|
2020-06-21 12:25:28 +00:00
|
|
|
updateHelpPrompts();
|
|
|
|
|
|
2022-11-25 17:57:06 +00:00
|
|
|
const float posMax {static_cast<float>(mPrimary->getNumEntries())};
|
|
|
|
|
const float target {static_cast<float>(cursor)};
|
2022-02-06 13:01:40 +00:00
|
|
|
float startPos {mCamOffset};
|
2022-03-24 22:05:23 +00:00
|
|
|
float endPos {target};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mPreviousScrollVelocity > 0 && scrollVelocity == 0 && mCamOffset > posMax - 1.0f)
|
|
|
|
|
startPos = 0.0f;
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mPrimaryType == PrimaryType::CAROUSEL) {
|
|
|
|
|
// Find the shortest path to the target.
|
2022-05-15 16:58:39 +00:00
|
|
|
float dist {fabsf(endPos - startPos)};
|
2022-03-24 22:05:23 +00:00
|
|
|
|
|
|
|
|
if (fabs(target + posMax - startPos - scrollVelocity) < dist)
|
|
|
|
|
endPos = target + posMax; // Loop around the end (0 -> max).
|
|
|
|
|
if (fabs(target - posMax - startPos - scrollVelocity) < dist)
|
|
|
|
|
endPos = target - posMax; // Loop around the start (max - 1 -> -1).
|
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-02-08 23:05:06 +00:00
|
|
|
// Make sure transitions do not animate in reverse.
|
|
|
|
|
bool changedDirection {false};
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mPreviousScrollVelocity != 0 && mPreviousScrollVelocity != scrollVelocity) {
|
|
|
|
|
if (scrollVelocity > 0 && startPos + scrollVelocity < posMax)
|
|
|
|
|
changedDirection = true;
|
|
|
|
|
}
|
2022-02-08 23:05:06 +00:00
|
|
|
|
|
|
|
|
if (!changedDirection && scrollVelocity > 0 && endPos < startPos)
|
|
|
|
|
endPos = endPos + posMax;
|
|
|
|
|
|
|
|
|
|
if (!changedDirection && scrollVelocity < 0 && endPos > startPos)
|
|
|
|
|
endPos = endPos - posMax;
|
|
|
|
|
|
|
|
|
|
if (scrollVelocity != 0)
|
|
|
|
|
mPreviousScrollVelocity = scrollVelocity;
|
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
Animation* anim;
|
2020-11-15 19:06:33 +00:00
|
|
|
|
2022-12-16 16:25:57 +00:00
|
|
|
float animTime {400.0f};
|
2022-12-05 22:12:41 +00:00
|
|
|
float timeMin {200.0f};
|
|
|
|
|
float timeDiff {1.0f};
|
2022-11-25 21:35:35 +00:00
|
|
|
|
|
|
|
|
if (mGrid != nullptr) {
|
2022-12-09 18:37:18 +00:00
|
|
|
animTime = 300.0f;
|
2022-12-05 22:12:41 +00:00
|
|
|
timeMin = 100.0f;
|
2022-11-25 21:35:35 +00:00
|
|
|
}
|
2022-12-05 22:12:41 +00:00
|
|
|
|
|
|
|
|
// If startPos is inbetween two positions then reduce the time slightly as the distance will
|
|
|
|
|
// be shorter meaning the animation would play for too long if not compensated for.
|
|
|
|
|
if (scrollVelocity == 1)
|
|
|
|
|
timeDiff = endPos - startPos;
|
|
|
|
|
else if (scrollVelocity == -1)
|
|
|
|
|
timeDiff = startPos - endPos;
|
|
|
|
|
|
|
|
|
|
if (timeDiff != 1.0f)
|
|
|
|
|
animTime =
|
|
|
|
|
glm::clamp(std::fabs(glm::mix(0.0f, animTime, timeDiff * 1.5f)), timeMin, animTime);
|
2022-11-12 13:08:53 +00:00
|
|
|
|
2023-01-08 16:00:36 +00:00
|
|
|
if (transitionAnim == ViewTransitionAnimation::FADE) {
|
2022-02-06 13:01:40 +00:00
|
|
|
float startFade {mFadeOpacity};
|
2020-06-21 12:25:28 +00:00
|
|
|
anim = new LambdaAnimation(
|
2024-09-15 09:34:28 +00:00
|
|
|
[this, startFade, endPos, prevLastCursor](float t) {
|
2021-07-07 18:03:42 +00:00
|
|
|
if (t < 0.3f)
|
2022-02-09 17:22:06 +00:00
|
|
|
mFadeOpacity =
|
2022-02-06 13:01:40 +00:00
|
|
|
glm::mix(0.0f, 1.0f, glm::clamp(t / 0.2f + startFade, 0.0f, 1.0f));
|
2021-07-07 18:03:42 +00:00
|
|
|
else if (t < 0.7f)
|
2022-02-09 17:22:06 +00:00
|
|
|
mFadeOpacity = 1.0f;
|
2021-07-07 18:03:42 +00:00
|
|
|
else
|
2022-02-09 17:22:06 +00:00
|
|
|
mFadeOpacity = glm::mix(1.0f, 0.0f, glm::clamp((t - 0.6f) / 0.3f, 0.0f, 1.0f));
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
if (t > 0.5f)
|
2022-02-09 17:22:06 +00:00
|
|
|
mCamOffset = endPos;
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-11-12 13:08:53 +00:00
|
|
|
if (mNavigated && t >= 0.7f && t != 1.0f)
|
2022-03-11 22:20:27 +00:00
|
|
|
mMaxFade = true;
|
|
|
|
|
|
2024-09-15 09:34:28 +00:00
|
|
|
if (t == 1.0f && prevLastCursor >= 0) {
|
|
|
|
|
for (auto& video : mSystemElements[prevLastCursor].videoComponents)
|
|
|
|
|
video->stopVideoPlayer(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
// Update the game count when the entire animation has been completed.
|
2022-03-11 22:20:27 +00:00
|
|
|
if (mFadeOpacity == 1.0f) {
|
|
|
|
|
mMaxFade = false;
|
2021-07-07 18:03:42 +00:00
|
|
|
updateGameCount();
|
2022-03-11 22:20:27 +00:00
|
|
|
}
|
2021-07-07 18:03:42 +00:00
|
|
|
},
|
2022-11-12 13:08:53 +00:00
|
|
|
static_cast<int>(animTime * 1.3f));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
2023-01-08 16:00:36 +00:00
|
|
|
else if (transitionAnim == ViewTransitionAnimation::SLIDE) {
|
2020-11-15 19:06:33 +00:00
|
|
|
mUpdatedGameCount = false;
|
2020-06-21 12:25:28 +00:00
|
|
|
anim = new LambdaAnimation(
|
2024-09-15 09:34:28 +00:00
|
|
|
[this, startPos, endPos, posMax, prevLastCursor](float t) {
|
2022-11-12 13:08:53 +00:00
|
|
|
// Non-linear interpolation.
|
|
|
|
|
t = 1.0f - (1.0f - t) * (1.0f - t);
|
|
|
|
|
float f {(endPos * t) + (startPos * (1.0f - t))};
|
2022-12-05 22:12:41 +00:00
|
|
|
|
2022-11-12 13:08:53 +00:00
|
|
|
if (f < 0)
|
2021-07-07 18:03:42 +00:00
|
|
|
f += posMax;
|
|
|
|
|
if (f >= posMax)
|
|
|
|
|
f -= posMax;
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
mCamOffset = f;
|
2021-07-07 18:03:42 +00:00
|
|
|
|
2024-09-15 09:34:28 +00:00
|
|
|
if (t == 1.0f && prevLastCursor >= 0) {
|
|
|
|
|
for (auto& video : mSystemElements[prevLastCursor].videoComponents)
|
|
|
|
|
video->stopVideoPlayer(false);
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
// Hack to make the game count being updated in the middle of the animation.
|
2022-02-06 13:01:40 +00:00
|
|
|
bool update {false};
|
2021-07-07 18:03:42 +00:00
|
|
|
if (endPos == -1.0f && fabs(fabs(posMax) - fabs(mCamOffset)) > 0.5f &&
|
|
|
|
|
!mUpdatedGameCount) {
|
|
|
|
|
update = true;
|
|
|
|
|
}
|
|
|
|
|
else if (endPos > posMax && fabs(endPos - posMax - fabs(mCamOffset)) < 0.5f &&
|
|
|
|
|
!mUpdatedGameCount) {
|
|
|
|
|
update = true;
|
|
|
|
|
}
|
|
|
|
|
else if (fabs(fabs(endPos) - fabs(mCamOffset)) < 0.5f && !mUpdatedGameCount) {
|
|
|
|
|
update = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (update) {
|
|
|
|
|
mUpdatedGameCount = true;
|
|
|
|
|
updateGameCount();
|
|
|
|
|
}
|
|
|
|
|
},
|
2022-11-12 13:08:53 +00:00
|
|
|
static_cast<int>(animTime));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Instant.
|
2020-11-15 19:06:33 +00:00
|
|
|
updateGameCount();
|
2022-12-11 16:02:21 +00:00
|
|
|
anim = new LambdaAnimation([this, endPos](float t) { mCamOffset = endPos; },
|
|
|
|
|
static_cast<int>(animTime));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setAnimation(anim, 0, nullptr, false, 0);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
void SystemView::populate()
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2022-03-28 15:47:34 +00:00
|
|
|
if (SystemData::sSystemVector.size() == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-12-07 18:43:03 +00:00
|
|
|
LOG(LogDebug) << "SystemView::populate(): Populating primary element...";
|
2022-02-09 17:22:06 +00:00
|
|
|
|
2023-08-14 20:40:32 +00:00
|
|
|
auto themes = ThemeData::getThemes();
|
|
|
|
|
std::map<std::string, ThemeData::Theme, ThemeData::StringComparator>::const_iterator
|
|
|
|
|
selectedTheme {themes.find(Settings::getInstance()->getString("Theme"))};
|
2017-05-27 07:40:18 +00:00
|
|
|
|
2023-08-14 20:40:32 +00:00
|
|
|
assert(selectedTheme != themes.cend());
|
2022-02-06 19:13:53 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
for (auto it : SystemData::sSystemVector) {
|
|
|
|
|
const std::shared_ptr<ThemeData>& theme {it->getTheme()};
|
2022-12-11 10:22:08 +00:00
|
|
|
std::string imagePath;
|
|
|
|
|
std::string defaultImagePath;
|
2022-12-07 18:43:03 +00:00
|
|
|
std::string itemText;
|
2021-10-23 15:34:20 +00:00
|
|
|
|
2023-07-30 16:17:27 +00:00
|
|
|
SystemViewElements elements;
|
|
|
|
|
elements.system = it;
|
|
|
|
|
if (theme->hasView("system")) {
|
2022-02-06 13:55:48 +00:00
|
|
|
elements.name = it->getName();
|
2023-07-30 16:17:27 +00:00
|
|
|
elements.fullName = it->getFullName();
|
|
|
|
|
for (auto& element : theme->getViewElements("system").elements) {
|
|
|
|
|
if (element.second.type == "gameselector") {
|
|
|
|
|
elements.gameSelectors.emplace_back(
|
|
|
|
|
std::make_unique<GameSelectorComponent>(it));
|
|
|
|
|
elements.gameSelectors.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.gameSelectors.back()->setNeedsRefresh();
|
|
|
|
|
}
|
|
|
|
|
if (element.second.type == "carousel" || element.second.type == "grid" ||
|
|
|
|
|
element.second.type == "textlist") {
|
|
|
|
|
if (element.second.type == "carousel" &&
|
|
|
|
|
(mGrid != nullptr || mTextList != nullptr)) {
|
|
|
|
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
|
|
|
|
<< "defined, skipping carousel configuration entry";
|
|
|
|
|
continue;
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
if (element.second.type == "grid" &&
|
|
|
|
|
(mCarousel != nullptr || mTextList != nullptr)) {
|
|
|
|
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
|
|
|
|
<< "defined, skipping grid configuration entry";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (element.second.type == "textlist" &&
|
|
|
|
|
(mCarousel != nullptr || mGrid != nullptr)) {
|
|
|
|
|
LOG(LogWarning) << "SystemView::populate(): Multiple primary components "
|
|
|
|
|
<< "defined, skipping textlist configuration entry";
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (element.second.type == "carousel" && mCarousel == nullptr) {
|
|
|
|
|
mCarousel = std::make_unique<CarouselComponent<SystemData*>>();
|
|
|
|
|
mPrimary = mCarousel.get();
|
|
|
|
|
mPrimaryType = PrimaryType::CAROUSEL;
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "grid" && mGrid == nullptr) {
|
|
|
|
|
mGrid = std::make_unique<GridComponent<SystemData*>>();
|
|
|
|
|
mPrimary = mGrid.get();
|
|
|
|
|
mPrimaryType = PrimaryType::GRID;
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "textlist" && mTextList == nullptr) {
|
|
|
|
|
mTextList = std::make_unique<TextListComponent<SystemData*>>();
|
|
|
|
|
mPrimary = mTextList.get();
|
|
|
|
|
mPrimaryType = PrimaryType::TEXTLIST;
|
|
|
|
|
}
|
|
|
|
|
mPrimary->setDefaultZIndex(50.0f);
|
|
|
|
|
mPrimary->applyTheme(theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
mPrimary->setCursorChangedCallback(
|
|
|
|
|
[&](const CursorState& state) { onCursorChanged(state); });
|
|
|
|
|
mPrimary->setCancelTransitionsCallback([&] {
|
|
|
|
|
ViewController::getInstance()->cancelViewTransitions();
|
|
|
|
|
mNavigated = true;
|
|
|
|
|
if (mSystemElements.size() > 1) {
|
|
|
|
|
for (auto& anim :
|
|
|
|
|
mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
|
|
|
|
for (auto& anim :
|
|
|
|
|
mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
2022-03-24 22:05:23 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
});
|
|
|
|
|
if (mCarousel != nullptr || mGrid != nullptr) {
|
|
|
|
|
if (element.second.has("staticImage"))
|
|
|
|
|
imagePath = element.second.get<std::string>("staticImage");
|
2023-09-08 17:34:05 +00:00
|
|
|
if (element.second.has("defaultImage") &&
|
|
|
|
|
Utils::FileSystem::exists(
|
|
|
|
|
element.second.get<std::string>("defaultImage")))
|
2023-07-30 16:17:27 +00:00
|
|
|
defaultImagePath = element.second.get<std::string>("defaultImage");
|
|
|
|
|
if (element.second.has("text"))
|
|
|
|
|
itemText = element.second.get<std::string>("text");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "image" &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
// If this is the first system then forceload to avoid texture pop-in.
|
|
|
|
|
if (it == SystemData::sSystemVector.front())
|
|
|
|
|
elements.imageComponents.emplace_back(
|
|
|
|
|
std::make_unique<ImageComponent>(true));
|
|
|
|
|
else
|
|
|
|
|
elements.imageComponents.emplace_back(std::make_unique<ImageComponent>());
|
|
|
|
|
|
|
|
|
|
elements.imageComponents.back()->setDefaultZIndex(30.0f);
|
|
|
|
|
elements.imageComponents.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.imageComponents.back().get());
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "video" &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
elements.videoComponents.emplace_back(std::make_unique<VideoFFmpegComponent>());
|
|
|
|
|
elements.videoComponents.back()->setDefaultZIndex(30.0f);
|
|
|
|
|
elements.videoComponents.back()->setStaticVideo();
|
|
|
|
|
elements.videoComponents.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.videoComponents.back().get());
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "animation" && element.second.has("path") &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
const std::string extension {
|
|
|
|
|
Utils::FileSystem::getExtension(element.second.get<std::string>("path"))};
|
|
|
|
|
if (extension == ".json") {
|
|
|
|
|
elements.lottieAnimComponents.emplace_back(
|
|
|
|
|
std::make_unique<LottieAnimComponent>());
|
|
|
|
|
elements.lottieAnimComponents.back()->setDefaultZIndex(35.0f);
|
|
|
|
|
elements.lottieAnimComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.lottieAnimComponents.back().get());
|
|
|
|
|
}
|
|
|
|
|
else if (extension == ".gif") {
|
|
|
|
|
elements.GIFAnimComponents.emplace_back(
|
|
|
|
|
std::make_unique<GIFAnimComponent>());
|
|
|
|
|
elements.GIFAnimComponents.back()->setDefaultZIndex(35.0f);
|
|
|
|
|
elements.GIFAnimComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.GIFAnimComponents.back().get());
|
2022-02-06 19:36:06 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
else if (extension == ".") {
|
|
|
|
|
LOG(LogWarning) << "SystemView::populate(): Invalid theme configuration, "
|
|
|
|
|
"animation file extension is missing";
|
2022-02-06 13:01:40 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
else {
|
|
|
|
|
LOG(LogWarning) << "SystemView::populate(): Invalid theme configuration, "
|
|
|
|
|
"animation file extension defined as \""
|
|
|
|
|
<< extension << "\"";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "text" &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
// Set as container by default if metadata type is "description".
|
|
|
|
|
bool container {false};
|
|
|
|
|
if (element.second.has("container")) {
|
|
|
|
|
container = element.second.get<bool>("container");
|
2023-08-07 20:58:35 +00:00
|
|
|
if (element.second.has("containerType") &&
|
|
|
|
|
element.second.get<std::string>("containerType") == "horizontal")
|
|
|
|
|
container = false;
|
2023-07-30 16:17:27 +00:00
|
|
|
}
|
|
|
|
|
else if (element.second.has("metadata") &&
|
|
|
|
|
element.second.get<std::string>("metadata") == "description") {
|
|
|
|
|
container = true;
|
2022-02-19 20:22:46 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
if (element.second.has("systemdata") &&
|
|
|
|
|
element.second.get<std::string>("systemdata").substr(0, 9) == "gamecount") {
|
2024-09-01 09:30:16 +00:00
|
|
|
// A vertical container can't be used if systemdata is set to a gamecount
|
|
|
|
|
// value. A horizontal container can be used though.
|
2023-07-30 16:17:27 +00:00
|
|
|
if (element.second.has("systemdata")) {
|
|
|
|
|
elements.gameCountComponents.emplace_back(
|
|
|
|
|
std::make_unique<TextComponent>());
|
|
|
|
|
elements.gameCountComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.gameCountComponents.back()->applyTheme(
|
2022-03-05 20:10:40 +00:00
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
2023-02-19 18:06:11 +00:00
|
|
|
elements.children.emplace_back(
|
2023-07-30 16:17:27 +00:00
|
|
|
elements.gameCountComponents.back().get());
|
2022-03-05 20:10:40 +00:00
|
|
|
}
|
2022-02-19 21:46:52 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
else {
|
|
|
|
|
if (container) {
|
|
|
|
|
elements.containerComponents.push_back(
|
|
|
|
|
std::make_unique<ScrollableContainer>());
|
|
|
|
|
elements.containerComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.containerTextComponents.push_back(
|
|
|
|
|
std::make_unique<TextComponent>());
|
|
|
|
|
elements.containerTextComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.containerComponents.back()->addChild(
|
|
|
|
|
elements.containerTextComponents.back().get());
|
|
|
|
|
elements.containerComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first,
|
|
|
|
|
ThemeFlags::POSITION | ThemeFlags::SIZE | ThemeFlags::Z_INDEX |
|
|
|
|
|
ThemeFlags::VISIBLE);
|
|
|
|
|
elements.containerComponents.back()->setAutoScroll(true);
|
|
|
|
|
elements.containerTextComponents.back()->setSize(
|
|
|
|
|
elements.containerComponents.back()->getSize().x, 0.0f);
|
|
|
|
|
elements.containerTextComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL ^ ThemeFlags::POSITION ^ ThemeFlags::ORIGIN ^
|
|
|
|
|
ThemeFlags::Z_INDEX ^ ThemeFlags::SIZE ^ ThemeFlags::VISIBLE ^
|
|
|
|
|
ThemeFlags::ROTATION);
|
|
|
|
|
elements.children.emplace_back(
|
|
|
|
|
elements.containerComponents.back().get());
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2023-07-30 16:17:27 +00:00
|
|
|
elements.textComponents.emplace_back(std::make_unique<TextComponent>());
|
|
|
|
|
elements.textComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.textComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.textComponents.back().get());
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
2022-02-06 13:01:40 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
}
|
|
|
|
|
else if (element.second.type == "datetime" &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
elements.dateTimeComponents.emplace_back(std::make_unique<DateTimeComponent>());
|
|
|
|
|
elements.dateTimeComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.dateTimeComponents.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.dateTimeComponents.back()->setVisible(false);
|
|
|
|
|
elements.children.emplace_back(elements.dateTimeComponents.back().get());
|
|
|
|
|
}
|
|
|
|
|
else if (element.second.type == "rating" &&
|
|
|
|
|
!(element.second.has("visible") && !element.second.get<bool>("visible"))) {
|
|
|
|
|
elements.ratingComponents.emplace_back(std::make_unique<RatingComponent>());
|
|
|
|
|
elements.ratingComponents.back()->setDefaultZIndex(45.0f);
|
|
|
|
|
elements.ratingComponents.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.ratingComponents.back()->setVisible(false);
|
|
|
|
|
elements.ratingComponents.back()->setOpacity(
|
|
|
|
|
elements.ratingComponents.back()->getOpacity());
|
|
|
|
|
elements.children.emplace_back(elements.ratingComponents.back().get());
|
2022-02-06 13:01:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-30 16:17:27 +00:00
|
|
|
std::stable_sort(
|
|
|
|
|
elements.children.begin(), elements.children.end(),
|
|
|
|
|
[](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); });
|
|
|
|
|
|
|
|
|
|
std::stable_sort(
|
|
|
|
|
elements.imageComponents.begin(), elements.imageComponents.end(),
|
|
|
|
|
[](const std::unique_ptr<ImageComponent>& a, const std::unique_ptr<ImageComponent>& b) {
|
|
|
|
|
return b->getZIndex() > a->getZIndex();
|
|
|
|
|
});
|
|
|
|
|
std::stable_sort(
|
|
|
|
|
elements.textComponents.begin(), elements.textComponents.end(),
|
|
|
|
|
[](const std::unique_ptr<TextComponent>& a, const std::unique_ptr<TextComponent>& b) {
|
|
|
|
|
return b->getZIndex() > a->getZIndex();
|
|
|
|
|
});
|
|
|
|
|
std::stable_sort(
|
|
|
|
|
elements.containerTextComponents.begin(), elements.containerTextComponents.end(),
|
|
|
|
|
[](const std::unique_ptr<TextComponent>& a, const std::unique_ptr<TextComponent>& b) {
|
|
|
|
|
return b->getZIndex() > a->getZIndex();
|
|
|
|
|
});
|
|
|
|
|
mSystemElements.emplace_back(std::move(elements));
|
|
|
|
|
mSystemElements.back().helpStyle.applyTheme(theme, "system");
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mPrimary == nullptr) {
|
|
|
|
|
mCarousel = std::make_unique<CarouselComponent<SystemData*>>();
|
|
|
|
|
mPrimary = mCarousel.get();
|
|
|
|
|
mPrimaryType = PrimaryType::CAROUSEL;
|
|
|
|
|
mPrimary->setDefaultZIndex(50.0f);
|
|
|
|
|
mPrimary->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
|
|
|
|
mPrimary->setCursorChangedCallback(
|
|
|
|
|
[&](const CursorState& state) { onCursorChanged(state); });
|
|
|
|
|
mPrimary->setCancelTransitionsCallback([&] {
|
|
|
|
|
ViewController::getInstance()->cancelViewTransitions();
|
|
|
|
|
mNavigated = true;
|
|
|
|
|
if (mSystemElements.size() > 1) {
|
|
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
|
|
|
|
for (auto& anim : mSystemElements[mPrimary->getCursor()].GIFAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2022-11-03 11:31:42 +00:00
|
|
|
auto letterCaseFunc = [&it, this](std::string& name) {
|
|
|
|
|
LetterCase letterCase {LetterCase::NONE};
|
2023-01-13 10:03:23 +00:00
|
|
|
if (it->isCustomCollection()) {
|
|
|
|
|
letterCase = mPrimary->getLetterCaseCustomCollections();
|
|
|
|
|
if (letterCase == LetterCase::UNDEFINED)
|
|
|
|
|
letterCase = mPrimary->getLetterCase();
|
|
|
|
|
}
|
|
|
|
|
else if (it->isCollection()) {
|
|
|
|
|
letterCase = mPrimary->getLetterCaseAutoCollections();
|
|
|
|
|
if (letterCase == LetterCase::UNDEFINED)
|
2022-11-03 11:31:42 +00:00
|
|
|
letterCase = mPrimary->getLetterCase();
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
letterCase = mPrimary->getLetterCase();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (letterCase == LetterCase::UPPERCASE)
|
|
|
|
|
name = Utils::String::toUpper(name);
|
|
|
|
|
else if (letterCase == LetterCase::LOWERCASE)
|
|
|
|
|
name = Utils::String::toLower(name);
|
2023-01-13 10:03:23 +00:00
|
|
|
else if (letterCase == LetterCase::CAPITALIZE)
|
2022-11-03 11:31:42 +00:00
|
|
|
name = Utils::String::toCapitalized(name);
|
|
|
|
|
};
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mCarousel != nullptr) {
|
|
|
|
|
CarouselComponent<SystemData*>::Entry entry;
|
2023-07-30 16:17:27 +00:00
|
|
|
if (itemText == "")
|
|
|
|
|
entry.name = it->getFullName();
|
|
|
|
|
else
|
|
|
|
|
entry.name = itemText;
|
2022-11-03 11:31:42 +00:00
|
|
|
letterCaseFunc(entry.name);
|
2022-03-24 22:05:23 +00:00
|
|
|
entry.object = it;
|
2022-12-11 10:22:08 +00:00
|
|
|
entry.data.imagePath = imagePath;
|
|
|
|
|
entry.data.defaultImagePath = defaultImagePath;
|
2022-03-24 22:05:23 +00:00
|
|
|
mCarousel->addEntry(entry, theme);
|
|
|
|
|
}
|
2022-11-12 13:08:53 +00:00
|
|
|
else if (mGrid != nullptr) {
|
|
|
|
|
GridComponent<SystemData*>::Entry entry;
|
2022-12-07 18:43:03 +00:00
|
|
|
if (itemText == "")
|
|
|
|
|
entry.name = it->getFullName();
|
|
|
|
|
else
|
|
|
|
|
entry.name = itemText;
|
2022-11-12 13:08:53 +00:00
|
|
|
letterCaseFunc(entry.name);
|
|
|
|
|
entry.object = it;
|
2022-12-11 10:22:08 +00:00
|
|
|
entry.data.imagePath = imagePath;
|
|
|
|
|
entry.data.defaultImagePath = defaultImagePath;
|
2022-11-12 13:08:53 +00:00
|
|
|
mGrid->addEntry(entry, theme);
|
|
|
|
|
}
|
2022-11-03 11:31:42 +00:00
|
|
|
else if (mTextList != nullptr) {
|
2022-03-24 22:05:23 +00:00
|
|
|
TextListComponent<SystemData*>::Entry entry;
|
|
|
|
|
entry.name = it->getFullName();
|
2022-11-03 11:31:42 +00:00
|
|
|
letterCaseFunc(entry.name);
|
2022-03-24 22:05:23 +00:00
|
|
|
entry.object = it;
|
2022-11-03 22:24:11 +00:00
|
|
|
entry.data.entryType = TextListEntryType::PRIMARY;
|
2022-03-24 22:05:23 +00:00
|
|
|
mTextList->addEntry(entry);
|
|
|
|
|
}
|
2023-03-05 12:35:35 +00:00
|
|
|
|
|
|
|
|
// Update the game counter here so the text doesn't pop in during initial navigation.
|
|
|
|
|
updateGameCount(it);
|
2021-10-23 15:34:20 +00:00
|
|
|
}
|
2022-02-06 13:01:40 +00:00
|
|
|
|
2022-11-25 21:07:36 +00:00
|
|
|
if (mGrid != nullptr)
|
|
|
|
|
mGrid->calculateLayout();
|
|
|
|
|
|
2024-09-01 10:27:23 +00:00
|
|
|
#if defined(GETTEXT_DUMMY_ENTRIES)
|
|
|
|
|
_p("theme", "all");
|
|
|
|
|
_p("theme", "all games");
|
|
|
|
|
_p("theme", "recent");
|
|
|
|
|
_p("theme", "last played");
|
|
|
|
|
_p("theme", "favorites");
|
|
|
|
|
_p("theme", "collections");
|
|
|
|
|
#endif
|
|
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
for (auto& elements : mSystemElements) {
|
|
|
|
|
for (auto& text : elements.textComponents) {
|
2022-02-13 10:45:06 +00:00
|
|
|
if (text->getThemeSystemdata() != "") {
|
2024-09-01 10:27:23 +00:00
|
|
|
const bool translate {elements.system->isCollection() &&
|
|
|
|
|
!elements.system->isCustomCollection()};
|
|
|
|
|
if (text->getThemeSystemdata() == "name") {
|
|
|
|
|
if (translate)
|
|
|
|
|
text->setValue(_p("theme", elements.name.c_str()));
|
|
|
|
|
else
|
|
|
|
|
text->setValue(elements.name);
|
|
|
|
|
}
|
|
|
|
|
else if (text->getThemeSystemdata() == "fullname") {
|
|
|
|
|
if (translate)
|
|
|
|
|
text->setValue(_p("theme", elements.fullName.c_str()));
|
|
|
|
|
else
|
|
|
|
|
text->setValue(elements.fullName);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-02-13 10:45:06 +00:00
|
|
|
text->setValue(text->getThemeSystemdata());
|
2024-09-01 10:27:23 +00:00
|
|
|
}
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2023-03-26 17:38:30 +00:00
|
|
|
for (auto& containerText : elements.containerTextComponents) {
|
|
|
|
|
if (containerText->getThemeSystemdata() != "") {
|
2024-09-01 10:27:23 +00:00
|
|
|
const bool translate {elements.system->isCollection() &&
|
|
|
|
|
!elements.system->isCustomCollection()};
|
|
|
|
|
if (containerText->getThemeSystemdata() == "name") {
|
|
|
|
|
if (translate)
|
|
|
|
|
containerText->setValue(_p("theme", elements.name.c_str()));
|
|
|
|
|
else
|
|
|
|
|
containerText->setValue(elements.name);
|
|
|
|
|
}
|
|
|
|
|
else if (containerText->getThemeSystemdata() == "fullname") {
|
|
|
|
|
if (translate)
|
|
|
|
|
containerText->setValue(_p("theme", elements.fullName.c_str()));
|
|
|
|
|
else
|
|
|
|
|
containerText->setValue(elements.fullName);
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-03-26 17:38:30 +00:00
|
|
|
containerText->setValue(containerText->getThemeSystemdata());
|
2024-09-01 10:27:23 +00:00
|
|
|
}
|
2023-03-26 17:38:30 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
2023-01-08 16:00:36 +00:00
|
|
|
mFadeTransitions = (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
|
|
|
|
"TransitionsSystemToSystem")) == ViewTransitionAnimation::FADE);
|
2017-03-13 21:11:07 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2023-03-05 12:35:35 +00:00
|
|
|
void SystemView::updateGameCount(SystemData* system)
|
2017-03-13 21:11:07 +00:00
|
|
|
{
|
2023-03-05 12:35:35 +00:00
|
|
|
SystemData* sourceSystem {system == nullptr ? mPrimary->getSelected() : system};
|
|
|
|
|
|
|
|
|
|
std::pair<unsigned int, unsigned int> gameCount {sourceSystem->getDisplayedGameCount()};
|
2022-02-06 13:55:48 +00:00
|
|
|
std::stringstream ss;
|
2022-02-06 19:13:53 +00:00
|
|
|
std::stringstream ssGames;
|
|
|
|
|
std::stringstream ssFavorites;
|
|
|
|
|
bool games {false};
|
2023-03-05 12:35:35 +00:00
|
|
|
const bool favoriteSystem {sourceSystem->getName() == "favorites"};
|
|
|
|
|
const bool recentSystem {sourceSystem->getName() == "recent"};
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2023-03-05 12:35:35 +00:00
|
|
|
if (sourceSystem->isCollection() && favoriteSystem) {
|
2024-08-31 11:17:11 +00:00
|
|
|
ss << Utils::String::format(_np("theme", "%i game", "%i games", gameCount.first),
|
|
|
|
|
gameCount.first);
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
2023-03-05 12:35:35 +00:00
|
|
|
else if (sourceSystem->isCollection() && recentSystem) {
|
2022-02-06 19:13:53 +00:00
|
|
|
// The "recent" gamelist has probably been trimmed after sorting, so we'll cap it at
|
|
|
|
|
// its maximum limit of 50 games.
|
2024-08-31 11:17:11 +00:00
|
|
|
const unsigned int count {gameCount.first > 50 ? 50 : gameCount.first};
|
|
|
|
|
ss << Utils::String::format(_np("theme", "%i game", "%i games", count), count);
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2024-08-31 11:17:11 +00:00
|
|
|
ss << Utils::String::format(_np("theme", "%i game", "%i games", gameCount.first),
|
|
|
|
|
gameCount.first)
|
|
|
|
|
<< " "
|
|
|
|
|
<< Utils::String::format(
|
|
|
|
|
_np("theme", "(%i favorite)", "(%i favorites)", gameCount.second),
|
|
|
|
|
gameCount.second);
|
|
|
|
|
ssGames << Utils::String::format(_np("theme", "%i game", "%i games", gameCount.first),
|
|
|
|
|
gameCount.first);
|
|
|
|
|
ssFavorites << Utils::String::format(
|
|
|
|
|
_np("theme", "%i favorite", "%i favorites", gameCount.second), gameCount.second);
|
2022-02-06 19:13:53 +00:00
|
|
|
games = true;
|
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2023-07-30 16:17:27 +00:00
|
|
|
auto elementsIt = std::find_if(mSystemElements.cbegin(), mSystemElements.cend(),
|
|
|
|
|
[sourceSystem](const SystemViewElements& systemElements) {
|
|
|
|
|
return systemElements.system == sourceSystem;
|
|
|
|
|
});
|
|
|
|
|
for (auto& gameCountComp : (*elementsIt).gameCountComponents) {
|
|
|
|
|
if (gameCountComp->getThemeSystemdata() == "gamecount") {
|
|
|
|
|
gameCountComp->setValue(ss.str());
|
|
|
|
|
}
|
|
|
|
|
else if (gameCountComp->getThemeSystemdata() == "gamecountGames") {
|
|
|
|
|
if (games)
|
|
|
|
|
gameCountComp->setValue(ssGames.str());
|
|
|
|
|
else
|
2023-01-30 17:40:28 +00:00
|
|
|
gameCountComp->setValue(ss.str());
|
2023-07-30 16:17:27 +00:00
|
|
|
}
|
|
|
|
|
else if (gameCountComp->getThemeSystemdata() == "gamecountGamesNoText") {
|
|
|
|
|
gameCountComp->setValue(std::to_string(gameCount.first));
|
|
|
|
|
}
|
|
|
|
|
else if (gameCountComp->getThemeSystemdata() == "gamecountFavorites") {
|
|
|
|
|
gameCountComp->setValue(ssFavorites.str());
|
|
|
|
|
}
|
|
|
|
|
else if (gameCountComp->getThemeSystemdata() == "gamecountFavoritesNoText") {
|
|
|
|
|
if (!favoriteSystem && !recentSystem) {
|
|
|
|
|
gameCountComp->setValue(std::to_string(gameCount.second));
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
2023-07-30 16:17:27 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameCountComp->setValue(gameCountComp->getThemeSystemdata());
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2017-03-13 21:11:07 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
void SystemView::updateGameSelectors()
|
2022-02-13 21:30:03 +00:00
|
|
|
{
|
2022-12-15 17:23:48 +00:00
|
|
|
const int cursor {mPrimary->getCursor()};
|
2022-02-13 21:30:03 +00:00
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
if (mSystemElements[cursor].gameSelectors.size() == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-12-15 17:23:48 +00:00
|
|
|
const bool multipleSelectors {mSystemElements[cursor].gameSelectors.size() > 1};
|
2022-02-14 18:32:07 +00:00
|
|
|
|
|
|
|
|
for (auto& image : mSystemElements[cursor].imageComponents) {
|
|
|
|
|
if (image->getThemeImageTypes().size() == 0)
|
|
|
|
|
continue;
|
|
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& imageSelector {image->getThemeGameSelector()};
|
|
|
|
|
if (imageSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but image element does not state which one to "
|
2022-02-19 20:22:46 +00:00
|
|
|
"use, selecting first entry";
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
2022-03-18 21:16:53 +00:00
|
|
|
if (selector->getSelectorName() == imageSelector) {
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector = selector.get();
|
2022-03-18 21:16:53 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< imageSelector << "\" defined for image element, selecting first entry";
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
2022-02-19 20:22:46 +00:00
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(image->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
2022-02-14 18:32:07 +00:00
|
|
|
std::string path;
|
|
|
|
|
for (auto& imageType : image->getThemeImageTypes()) {
|
|
|
|
|
if (imageType == "image") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getImagePath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "miximage") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getMiximagePath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "marquee") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getMarqueePath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "screenshot") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getScreenshotPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "titlescreen") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getTitleScreenPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
else if (imageType == "cover") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getCoverPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "backcover") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getBackCoverPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "3dbox") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->get3DBoxPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 15:32:42 +00:00
|
|
|
else if (imageType == "physicalmedia") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getPhysicalMediaPath();
|
2022-06-07 15:32:42 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
else if (imageType == "fanart") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getFanArtPath();
|
2022-02-14 18:32:07 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// This is needed so the default image is set if no game media was found.
|
|
|
|
|
if (path == "" && image->getThemeImageTypes().size() > 0)
|
|
|
|
|
image->setImage("");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
image->setImage("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 20:22:46 +00:00
|
|
|
for (auto& video : mSystemElements[cursor].videoComponents) {
|
|
|
|
|
// If a static video has been set, then don't attempt to find a gameselector entry.
|
2023-08-13 09:17:59 +00:00
|
|
|
if (video->hasStaticVideo() || video->getThemeGameSelector() == ":none:")
|
2022-02-19 20:22:46 +00:00
|
|
|
continue;
|
|
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& videoSelector {video->getThemeGameSelector()};
|
|
|
|
|
if (videoSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but video element does not state which one to "
|
|
|
|
|
"use, selecting first entry";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
2022-03-18 21:16:53 +00:00
|
|
|
if (selector->getSelectorName() == videoSelector) {
|
2022-02-19 20:22:46 +00:00
|
|
|
gameSelector = selector.get();
|
2022-03-18 21:16:53 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
}
|
|
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< videoSelector << "\" defined for video element, selecting first entry";
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(video->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-02-19 20:22:46 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
|
|
|
|
if (!video->setVideo(games.at(gameSelectorEntry)->getVideoPath()))
|
2022-02-19 20:22:46 +00:00
|
|
|
video->setDefaultVideo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& video : mSystemElements[cursor].videoComponents) {
|
2023-08-13 09:17:59 +00:00
|
|
|
if (video->hasStaticVideo() || video->getThemeGameSelector() == ":none:" ||
|
2022-12-21 18:53:03 +00:00
|
|
|
(video->getThemeImageTypes().size() == 0 && video->getDefaultImage() == ""))
|
2022-02-19 20:22:46 +00:00
|
|
|
continue;
|
|
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& imageSelector {video->getThemeGameSelector()};
|
|
|
|
|
if (imageSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but video element does not state which one to "
|
|
|
|
|
"use, selecting first entry";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
2022-03-18 21:16:53 +00:00
|
|
|
if (selector->getSelectorName() == imageSelector) {
|
2022-02-19 20:22:46 +00:00
|
|
|
gameSelector = selector.get();
|
2022-03-18 21:16:53 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
}
|
|
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< imageSelector << "\" defined for video element, selecting first entry";
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(video->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-02-19 20:22:46 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
2022-02-19 20:22:46 +00:00
|
|
|
std::string path;
|
|
|
|
|
for (auto& imageType : video->getThemeImageTypes()) {
|
|
|
|
|
if (imageType == "image") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getImagePath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "miximage") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getMiximagePath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "marquee") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getMarqueePath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "screenshot") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getScreenshotPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "titlescreen") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getTitleScreenPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "cover") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getCoverPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "backcover") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getBackCoverPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "3dbox") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->get3DBoxPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-06-07 15:32:42 +00:00
|
|
|
else if (imageType == "physicalmedia") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getPhysicalMediaPath();
|
2022-06-07 15:32:42 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
else if (imageType == "fanart") {
|
2022-12-15 17:23:48 +00:00
|
|
|
path = games.at(gameSelectorEntry)->getFanArtPath();
|
2022-02-19 20:22:46 +00:00
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// This is needed so the default image is set if no game media was found.
|
2022-12-21 18:53:03 +00:00
|
|
|
if (path == "" &&
|
|
|
|
|
(video->getThemeImageTypes().size() > 0 || video->getDefaultImage() != ""))
|
2022-02-19 20:22:46 +00:00
|
|
|
video->setImage("");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
video->setImage("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-26 17:38:30 +00:00
|
|
|
auto textSelectorFunc = [this, cursor,
|
|
|
|
|
multipleSelectors](std::unique_ptr<TextComponent>& text) {
|
2022-02-14 18:32:07 +00:00
|
|
|
if (text->getThemeMetadata() == "")
|
2023-03-26 17:38:30 +00:00
|
|
|
return;
|
2022-02-14 18:32:07 +00:00
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& textSelector {text->getThemeGameSelector()};
|
|
|
|
|
if (textSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but text element does not state which one to "
|
2022-02-20 14:49:32 +00:00
|
|
|
"use, selecting first entry";
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
2022-03-18 21:16:53 +00:00
|
|
|
if (selector->getSelectorName() == textSelector) {
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector = selector.get();
|
2022-03-18 21:16:53 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
2022-02-20 14:49:32 +00:00
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< textSelector << "\" defined for text element, selecting first entry";
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
2022-02-20 14:49:32 +00:00
|
|
|
}
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(text->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-02-14 18:32:07 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
2022-02-14 18:32:07 +00:00
|
|
|
const std::string metadata {text->getThemeMetadata()};
|
2023-01-14 13:05:24 +00:00
|
|
|
if (metadata == "name") {
|
|
|
|
|
if (mPrimary->getSelected()->isCollection() && text->getSystemNameSuffix()) {
|
|
|
|
|
const LetterCase letterCase {text->getLetterCaseSystemNameSuffix()};
|
|
|
|
|
std::string suffix {" ["};
|
|
|
|
|
if (letterCase == LetterCase::UPPERCASE) {
|
|
|
|
|
suffix.append(Utils::String::toUpper(games.at(gameSelectorEntry)
|
|
|
|
|
->getSourceFileData()
|
|
|
|
|
->getSystem()
|
|
|
|
|
->getName()));
|
|
|
|
|
}
|
|
|
|
|
else if (letterCase == LetterCase::CAPITALIZE) {
|
|
|
|
|
suffix.append(Utils::String::toCapitalized(games.at(gameSelectorEntry)
|
|
|
|
|
->getSourceFileData()
|
|
|
|
|
->getSystem()
|
|
|
|
|
->getName()));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
suffix.append(games.at(gameSelectorEntry)
|
|
|
|
|
->getSourceFileData()
|
|
|
|
|
->getSystem()
|
|
|
|
|
->getName());
|
|
|
|
|
}
|
|
|
|
|
suffix.append("]");
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("name") + suffix);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("name"));
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "description")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("desc"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "rating")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(RatingComponent::getRatingValue(
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("rating")));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "developer")
|
2024-09-04 15:27:34 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("developer") == "unknown" ?
|
|
|
|
|
_p("theme", "unknown") :
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("developer"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "publisher")
|
2024-09-04 15:27:34 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("publisher") == "unknown" ?
|
|
|
|
|
_p("theme", "unknown") :
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("publisher"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "genre")
|
2024-09-04 15:27:34 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("genre") == "unknown" ?
|
|
|
|
|
_p("theme", "unknown") :
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("genre"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "players")
|
2024-09-04 15:27:34 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("players") == "unknown" ?
|
|
|
|
|
_p("theme", "unknown") :
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("players"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "favorite")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("favorite") == "true" ? "yes" : "no");
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "completed")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("completed") == "true" ?
|
|
|
|
|
"yes" :
|
|
|
|
|
"no");
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "kidgame")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("kidgame") == "true" ? "yes" : "no");
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "broken")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("broken") == "true" ? "yes" : "no");
|
2023-07-20 15:33:49 +00:00
|
|
|
else if (metadata == "manual")
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->getManualPath() != "" ? "yes" : "no");
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "playcount")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("playcount"));
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "altemulator")
|
2022-12-15 17:23:48 +00:00
|
|
|
text->setValue(games.at(gameSelectorEntry)->metadata.get("altemulator"));
|
2023-02-19 10:50:47 +00:00
|
|
|
else if (metadata == "emulator")
|
|
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("altemulator") != "" ?
|
|
|
|
|
games.at(gameSelectorEntry)->metadata.get("altemulator") :
|
2023-02-19 12:49:16 +00:00
|
|
|
(games.at(gameSelectorEntry)->getSourceSystem()->getAlternativeEmulator() !=
|
|
|
|
|
"" ?
|
2023-02-19 10:50:47 +00:00
|
|
|
games.at(gameSelectorEntry)
|
2023-02-19 12:49:16 +00:00
|
|
|
->getSourceSystem()
|
|
|
|
|
->getAlternativeEmulator() :
|
|
|
|
|
games.at(gameSelectorEntry)
|
|
|
|
|
->getSourceSystem()
|
2023-02-19 10:50:47 +00:00
|
|
|
->getSystemEnvData()
|
|
|
|
|
->mLaunchCommands.front()
|
|
|
|
|
.second));
|
2023-09-23 10:52:00 +00:00
|
|
|
else if (metadata == "physicalName")
|
|
|
|
|
text->setValue(
|
|
|
|
|
Utils::FileSystem::getStem(games.at(gameSelectorEntry)->getFileName()));
|
|
|
|
|
else if (metadata == "physicalNameExtension")
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->getFileName());
|
2023-01-15 11:51:59 +00:00
|
|
|
else if (metadata == "systemName")
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->getSystem()->getName());
|
|
|
|
|
else if (metadata == "systemFullname")
|
|
|
|
|
text->setValue(games.at(gameSelectorEntry)->getSystem()->getFullName());
|
|
|
|
|
else if (metadata == "sourceSystemName")
|
|
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->getSourceFileData()->getSystem()->getName());
|
|
|
|
|
else if (metadata == "sourceSystemFullname")
|
|
|
|
|
text->setValue(
|
|
|
|
|
games.at(gameSelectorEntry)->getSourceFileData()->getSystem()->getFullName());
|
2022-02-14 18:32:07 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
text->setValue("");
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
2023-03-26 17:38:30 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (auto& text : mSystemElements[cursor].textComponents)
|
|
|
|
|
textSelectorFunc(text);
|
|
|
|
|
|
|
|
|
|
for (auto& containerText : mSystemElements[cursor].containerTextComponents)
|
|
|
|
|
textSelectorFunc(containerText);
|
2022-02-20 14:49:32 +00:00
|
|
|
|
|
|
|
|
for (auto& dateTime : mSystemElements[cursor].dateTimeComponents) {
|
|
|
|
|
if (dateTime->getThemeMetadata() == "")
|
|
|
|
|
continue;
|
|
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& dateTimeSelector {dateTime->getThemeGameSelector()};
|
|
|
|
|
if (dateTimeSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but datetime element does not state which one "
|
|
|
|
|
"to use, selecting first entry";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
2022-03-18 21:16:53 +00:00
|
|
|
if (selector->getSelectorName() == dateTimeSelector) {
|
2022-02-20 14:49:32 +00:00
|
|
|
gameSelector = selector.get();
|
2022-03-18 21:16:53 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2022-02-20 14:49:32 +00:00
|
|
|
}
|
|
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning) << "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< dateTimeSelector
|
|
|
|
|
<< "\" defined for datetime element, selecting first entry";
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(dateTime->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-02-20 14:49:32 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
2022-03-18 21:16:53 +00:00
|
|
|
dateTime->setVisible(true);
|
2022-02-20 14:49:32 +00:00
|
|
|
const std::string metadata {dateTime->getThemeMetadata()};
|
|
|
|
|
if (metadata == "releasedate")
|
2022-12-15 17:23:48 +00:00
|
|
|
dateTime->setValue(games.at(gameSelectorEntry)->metadata.get("releasedate"));
|
2022-02-20 14:49:32 +00:00
|
|
|
if (metadata == "lastplayed")
|
2022-12-15 17:23:48 +00:00
|
|
|
dateTime->setValue(games.at(gameSelectorEntry)->metadata.get("lastplayed"));
|
2022-02-20 14:49:32 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2022-03-18 21:16:53 +00:00
|
|
|
dateTime->setVisible(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& rating : mSystemElements[cursor].ratingComponents) {
|
|
|
|
|
GameSelectorComponent* gameSelector {nullptr};
|
|
|
|
|
if (multipleSelectors) {
|
|
|
|
|
const std::string& ratingSelector {rating->getThemeGameSelector()};
|
|
|
|
|
if (ratingSelector == "") {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Multiple gameselector "
|
|
|
|
|
"elements defined but rating element does not state which one to "
|
|
|
|
|
"use, selecting first entry";
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
|
|
|
|
if (selector->getSelectorName() == ratingSelector) {
|
|
|
|
|
gameSelector = selector.get();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (gameSelector == nullptr) {
|
|
|
|
|
LOG(LogWarning)
|
|
|
|
|
<< "SystemView::updateGameSelectors(): Invalid gameselector \""
|
|
|
|
|
<< ratingSelector << "\" defined for rating element, selecting first entry";
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
2022-12-15 17:23:48 +00:00
|
|
|
const size_t gameSelectorEntry {static_cast<size_t>(
|
|
|
|
|
glm::clamp(rating->getThemeGameSelectorEntry(), 0u,
|
|
|
|
|
static_cast<unsigned int>(gameSelector->getGameCount() - 1)))};
|
2022-03-18 21:16:53 +00:00
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-12-15 17:23:48 +00:00
|
|
|
if (games.size() > gameSelectorEntry) {
|
2022-03-18 21:16:53 +00:00
|
|
|
rating->setVisible(true);
|
2022-12-15 17:23:48 +00:00
|
|
|
rating->setValue(games.at(gameSelectorEntry)->metadata.get("rating"));
|
2022-03-18 21:16:53 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
rating->setVisible(false);
|
2022-02-20 14:49:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
void SystemView::renderElements(const glm::mat4& parentTrans, bool abovePrimary)
|
2017-04-22 14:15:16 +00:00
|
|
|
{
|
2022-02-09 17:22:06 +00:00
|
|
|
glm::mat4 trans {getTransform() * parentTrans};
|
|
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
const float primaryZIndex {mPrimary->getZIndex()};
|
2022-02-09 17:22:06 +00:00
|
|
|
|
2022-03-24 22:05:23 +00:00
|
|
|
int renderBefore {static_cast<int>(mCamOffset)};
|
|
|
|
|
int renderAfter {static_cast<int>(mCamOffset)};
|
2022-03-05 20:10:40 +00:00
|
|
|
|
2023-08-06 21:51:53 +00:00
|
|
|
const ViewController::State viewState {ViewController::getInstance()->getState()};
|
|
|
|
|
|
|
|
|
|
// If we're transitioning between systems, then also render the previous and next systems.
|
|
|
|
|
if (isAnimationPlaying(0) && viewState.viewing == ViewController::ViewMode::SYSTEM_SELECT) {
|
2022-03-24 22:05:23 +00:00
|
|
|
renderBefore -= 1;
|
|
|
|
|
renderAfter += 1;
|
2022-03-05 20:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
2023-08-06 21:51:53 +00:00
|
|
|
bool stationaryApplicable {false};
|
|
|
|
|
|
|
|
|
|
// If it's the startup animation, then don't apply stationary properties.
|
|
|
|
|
if (viewState.previouslyViewed == ViewController::ViewMode::NOTHING)
|
|
|
|
|
stationaryApplicable = false;
|
|
|
|
|
|
|
|
|
|
// If it's a system to system transition and these animations are set to slide.
|
|
|
|
|
if (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
|
|
|
|
"TransitionsSystemToSystem")) == ViewTransitionAnimation::SLIDE &&
|
|
|
|
|
isAnimationPlaying(0))
|
|
|
|
|
stationaryApplicable = true;
|
|
|
|
|
|
|
|
|
|
// If it's a system to gamelist transition and these animations are set to slide.
|
|
|
|
|
if (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
|
|
|
|
"TransitionsSystemToGamelist")) == ViewTransitionAnimation::SLIDE &&
|
|
|
|
|
viewState.viewing == ViewController::ViewMode::GAMELIST)
|
|
|
|
|
stationaryApplicable = true;
|
|
|
|
|
|
|
|
|
|
// If it's a gamelist to system transition and these animations are set to slide.
|
|
|
|
|
if (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
|
|
|
|
"TransitionsGamelistToSystem")) == ViewTransitionAnimation::SLIDE &&
|
2023-12-17 20:18:08 +00:00
|
|
|
viewState.previouslyViewed == ViewController::ViewMode::GAMELIST &&
|
|
|
|
|
ViewController::getInstance()->isCameraMoving())
|
2023-08-06 21:51:53 +00:00
|
|
|
stationaryApplicable = true;
|
|
|
|
|
|
2023-07-30 16:17:27 +00:00
|
|
|
for (int i {renderBefore}; i <= renderAfter; ++i) {
|
2022-02-09 17:22:06 +00:00
|
|
|
int index {i};
|
|
|
|
|
while (index < 0)
|
2022-03-24 22:05:23 +00:00
|
|
|
index += static_cast<int>(mPrimary->getNumEntries());
|
|
|
|
|
while (index >= static_cast<int>(mPrimary->getNumEntries()))
|
|
|
|
|
index -= static_cast<int>(mPrimary->getNumEntries());
|
2022-02-09 17:22:06 +00:00
|
|
|
|
2022-12-05 22:12:41 +00:00
|
|
|
if (isAnimationPlaying(0) || index == mPrimary->getCursor()) {
|
2022-02-09 17:22:06 +00:00
|
|
|
glm::mat4 elementTrans {trans};
|
2022-03-24 22:05:23 +00:00
|
|
|
if (mCarousel != nullptr) {
|
|
|
|
|
if (mCarousel->getType() ==
|
|
|
|
|
CarouselComponent<SystemData*>::CarouselType::HORIZONTAL ||
|
|
|
|
|
mCarousel->getType() ==
|
|
|
|
|
CarouselComponent<SystemData*>::CarouselType::HORIZONTAL_WHEEL)
|
|
|
|
|
elementTrans = glm::translate(
|
2022-09-05 18:13:47 +00:00
|
|
|
elementTrans,
|
|
|
|
|
glm::round(glm::vec3 {(i - mCamOffset) * mSize.x, 0.0f, 0.0f}));
|
2022-03-24 22:05:23 +00:00
|
|
|
else
|
|
|
|
|
elementTrans = glm::translate(
|
2022-09-05 18:13:47 +00:00
|
|
|
elementTrans,
|
|
|
|
|
glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
2022-11-12 13:08:53 +00:00
|
|
|
}
|
|
|
|
|
else if (mGrid != nullptr) {
|
|
|
|
|
elementTrans = glm::translate(
|
|
|
|
|
elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
2022-03-24 22:05:23 +00:00
|
|
|
}
|
|
|
|
|
else if (mTextList != nullptr) {
|
2022-09-05 18:13:47 +00:00
|
|
|
elementTrans = glm::translate(
|
|
|
|
|
elementTrans, glm::round(glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f}));
|
2022-03-24 22:05:23 +00:00
|
|
|
}
|
2022-02-09 17:22:06 +00:00
|
|
|
|
2023-08-06 21:51:53 +00:00
|
|
|
auto clipRectFunc = [this, elementTrans]() {
|
|
|
|
|
mRenderer->pushClipRect(
|
|
|
|
|
glm::ivec2 {static_cast<int>(glm::round(elementTrans[3].x)),
|
|
|
|
|
static_cast<int>(glm::round(elementTrans[3].y))},
|
|
|
|
|
glm::ivec2 {static_cast<int>(mSize.x), static_cast<int>(mSize.y)});
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-19 16:35:58 +00:00
|
|
|
auto renderChildCondFunc = [&viewState](GuiComponent* child, glm::mat4 trans) {
|
2023-12-18 21:13:01 +00:00
|
|
|
bool renderChild {false};
|
|
|
|
|
if (!ViewController::getInstance()->isCameraMoving())
|
|
|
|
|
renderChild = true;
|
|
|
|
|
else if (viewState.previouslyViewed == ViewController::ViewMode::NOTHING)
|
|
|
|
|
renderChild = true;
|
|
|
|
|
else if (viewState.viewing == viewState.previouslyViewed)
|
|
|
|
|
renderChild = true;
|
|
|
|
|
else if (static_cast<ViewTransitionAnimation>(Settings::getInstance()->getInt(
|
|
|
|
|
"TransitionsSystemToGamelist")) != ViewTransitionAnimation::SLIDE &&
|
|
|
|
|
viewState.viewing == ViewController::ViewMode::GAMELIST)
|
|
|
|
|
renderChild = true;
|
|
|
|
|
if (renderChild)
|
2023-12-18 00:16:32 +00:00
|
|
|
child->render(trans);
|
|
|
|
|
};
|
|
|
|
|
|
2023-08-06 21:51:53 +00:00
|
|
|
clipRectFunc();
|
2022-02-09 17:22:06 +00:00
|
|
|
|
2023-07-30 16:17:27 +00:00
|
|
|
if (mSystemElements.size() > static_cast<size_t>(index)) {
|
2023-12-18 00:16:32 +00:00
|
|
|
for (GuiComponent* child : mSystemElements[index].children) {
|
2023-08-06 21:51:53 +00:00
|
|
|
bool renderChild {true};
|
|
|
|
|
bool childStationary {false};
|
|
|
|
|
if (stationaryApplicable) {
|
|
|
|
|
if (child->getStationary() == Stationary::NEVER) {
|
|
|
|
|
childStationary = false;
|
|
|
|
|
}
|
|
|
|
|
else if ((child->getStationary() == Stationary::WITHIN_VIEW ||
|
|
|
|
|
child->getStationary() == Stationary::ALWAYS) &&
|
|
|
|
|
isAnimationPlaying(0)) {
|
|
|
|
|
childStationary = true;
|
|
|
|
|
if (index != static_cast<int>(std::round(mCamOffset))) {
|
|
|
|
|
if (mCamOffset <= mSystemElements.size() - 1)
|
|
|
|
|
renderChild = false;
|
|
|
|
|
if (mCamOffset > static_cast<float>(mSystemElements.size() - 1) &&
|
|
|
|
|
index != 0)
|
|
|
|
|
renderChild = false;
|
|
|
|
|
if (mCamOffset <
|
|
|
|
|
static_cast<float>(mSystemElements.size()) - 0.5f &&
|
|
|
|
|
index == 0)
|
|
|
|
|
renderChild = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if ((child->getStationary() == Stationary::BETWEEN_VIEWS ||
|
|
|
|
|
child->getStationary() == Stationary::ALWAYS) &&
|
|
|
|
|
!isAnimationPlaying(0)) {
|
|
|
|
|
childStationary = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-26 18:02:31 +00:00
|
|
|
if (abovePrimary && (child->getZIndex() > primaryZIndex)) {
|
|
|
|
|
if (mFadeTransitions && mPrimary->getFadeAbovePrimary()) {
|
|
|
|
|
if (mFadeTransitions || child->getOpacity() != 1.0f)
|
|
|
|
|
child->setOpacity(1.0f - mFadeOpacity);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
child->setOpacity(1.0f);
|
|
|
|
|
}
|
2023-08-06 21:51:53 +00:00
|
|
|
if (renderChild) {
|
|
|
|
|
if (childStationary) {
|
|
|
|
|
mRenderer->popClipRect();
|
2023-12-18 21:13:01 +00:00
|
|
|
if (child->getRenderDuringTransitions())
|
|
|
|
|
child->render(mRenderer->getIdentity());
|
|
|
|
|
else
|
|
|
|
|
renderChildCondFunc(child, mRenderer->getIdentity());
|
2023-08-06 21:51:53 +00:00
|
|
|
clipRectFunc();
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-18 21:13:01 +00:00
|
|
|
if (child->getRenderDuringTransitions())
|
|
|
|
|
child->render(elementTrans);
|
|
|
|
|
else
|
|
|
|
|
renderChildCondFunc(child, elementTrans);
|
2023-08-06 21:51:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-11 22:20:27 +00:00
|
|
|
}
|
|
|
|
|
else if (!abovePrimary && child->getZIndex() <= primaryZIndex) {
|
2022-03-11 22:51:41 +00:00
|
|
|
if (mFadeTransitions || child->getDimming() != 1.0f)
|
|
|
|
|
child->setDimming(1.0f - mFadeOpacity);
|
2023-08-06 21:51:53 +00:00
|
|
|
if (renderChild) {
|
|
|
|
|
if (childStationary) {
|
|
|
|
|
mRenderer->popClipRect();
|
2023-12-18 21:13:01 +00:00
|
|
|
if (child->getRenderDuringTransitions())
|
|
|
|
|
child->render(mRenderer->getIdentity());
|
|
|
|
|
else
|
|
|
|
|
renderChildCondFunc(child, mRenderer->getIdentity());
|
2023-08-06 21:51:53 +00:00
|
|
|
clipRectFunc();
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-12-18 21:13:01 +00:00
|
|
|
if (child->getRenderDuringTransitions())
|
|
|
|
|
child->render(elementTrans);
|
|
|
|
|
else
|
|
|
|
|
renderChildCondFunc(child, elementTrans);
|
2023-08-06 21:51:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
2022-03-11 22:20:27 +00:00
|
|
|
}
|
2022-02-09 17:22:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-03-14 18:51:48 +00:00
|
|
|
mRenderer->popClipRect();
|
2022-02-09 17:22:06 +00:00
|
|
|
}
|
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|