2020-09-15 20:57:54 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-06 14:48:05 +00:00
|
|
|
//
|
2020-09-15 20:57:54 +00:00
|
|
|
// EmulationStation Desktop Edition
|
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"
|
|
|
|
|
#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
|
|
|
namespace
|
|
|
|
|
{
|
|
|
|
|
// Buffer values for scrolling velocity (left, stopped, right).
|
2022-02-09 17:22:06 +00:00
|
|
|
const int logoBuffersLeft[] {-5, -2, -1};
|
|
|
|
|
const int logoBuffersRight[] {1, 2, 5};
|
2022-01-19 17:01:54 +00:00
|
|
|
|
|
|
|
|
} // namespace
|
2017-05-17 12:01:15 +00:00
|
|
|
|
2022-01-19 17:01:54 +00:00
|
|
|
SystemView::SystemView()
|
2022-02-06 13:01:40 +00:00
|
|
|
: mCamOffset {0.0f}
|
|
|
|
|
, 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-01-30 20:16:03 +00:00
|
|
|
, mLegacyMode {false}
|
2022-02-19 20:22:46 +00:00
|
|
|
, mHoldingKey {false}
|
|
|
|
|
, mNavigated {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 13:01:40 +00:00
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
mCarousel = std::make_unique<CarouselComponent>();
|
2022-02-06 13:01:40 +00:00
|
|
|
mCarousel->setCursorChangedCallback([&](const CursorState& state) { onCursorChanged(state); });
|
2022-02-19 20:22:46 +00:00
|
|
|
mCarousel->setCancelTransitionsCallback([&] {
|
|
|
|
|
ViewController::getInstance()->cancelViewTransitions();
|
|
|
|
|
mNavigated = true;
|
2022-02-19 21:46:52 +00:00
|
|
|
for (auto& anim : mSystemElements[mCarousel->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
2022-02-19 20:22:46 +00:00
|
|
|
});
|
2022-02-06 19:13:53 +00:00
|
|
|
|
|
|
|
|
populate();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2020-08-15 07:28:47 +00:00
|
|
|
SystemView::~SystemView()
|
|
|
|
|
{
|
2022-02-06 13:01:40 +00:00
|
|
|
if (mLegacyMode) {
|
|
|
|
|
// Delete any existing extras.
|
2022-02-06 19:13:53 +00:00
|
|
|
for (auto& entry : mSystemElements) {
|
2022-02-06 13:01:40 +00:00
|
|
|
for (auto extra : entry.legacyExtras)
|
|
|
|
|
delete extra;
|
|
|
|
|
entry.legacyExtras.clear();
|
|
|
|
|
}
|
2020-08-15 07:28:47 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 21:46:52 +00:00
|
|
|
void SystemView::onTransition()
|
|
|
|
|
{
|
|
|
|
|
for (auto& anim : mSystemElements[mCarousel->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->setPauseAnimation(true);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
void SystemView::goToSystem(SystemData* system, bool animate)
|
|
|
|
|
{
|
2022-02-06 13:01:40 +00:00
|
|
|
mCarousel->setCursor(system);
|
2022-02-14 18:32:07 +00:00
|
|
|
|
|
|
|
|
for (auto& selector : mSystemElements[mCarousel->getCursor()].gameSelectors) {
|
|
|
|
|
if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM)
|
|
|
|
|
selector->setNeedsRefresh();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 20:22:46 +00:00
|
|
|
for (auto& video : mSystemElements[mCarousel->getCursor()].videoComponents)
|
|
|
|
|
video->setStaticVideo();
|
|
|
|
|
|
2022-02-19 21:46:52 +00:00
|
|
|
for (auto& anim : mSystemElements[mCarousel->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->resetFileAnimation();
|
|
|
|
|
|
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) {
|
2022-02-19 20:22:46 +00:00
|
|
|
mHoldingKey = true;
|
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
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-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-02-06 13:01:40 +00:00
|
|
|
mCarousel->stopScrolling();
|
2022-02-19 20:22:46 +00:00
|
|
|
pauseViewVideos();
|
2022-02-06 13:01:40 +00:00
|
|
|
ViewController::getInstance()->goToGamelist(mCarousel->getSelected());
|
2021-11-15 21:43:06 +00:00
|
|
|
NavigationSounds::getInstance().playThemeNavigationSound(SELECTSOUND);
|
2020-06-21 12:25:28 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2021-05-16 12:03:13 +00:00
|
|
|
if (Settings::getInstance()->getBool("RandomAddButton") &&
|
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-02-06 13:01:40 +00:00
|
|
|
mCarousel->setCursor(SystemData::getRandomSystem(mCarousel->getSelected()));
|
2020-06-21 12:25:28 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
2020-09-18 16:40:22 +00:00
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
if (!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("back", input) &&
|
|
|
|
|
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();
|
2020-11-10 21:18:20 +00:00
|
|
|
mWindow->startScreensaver();
|
|
|
|
|
mWindow->renderScreensaver();
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
else {
|
|
|
|
|
mHoldingKey = false;
|
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-02-06 13:01:40 +00:00
|
|
|
return mCarousel->input(config, input);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SystemView::update(int deltaTime)
|
|
|
|
|
{
|
2022-02-06 13:01:40 +00:00
|
|
|
mCarousel->update(deltaTime);
|
2022-02-19 20:22:46 +00:00
|
|
|
|
|
|
|
|
for (auto& video : mSystemElements[mCarousel->getCursor()].videoComponents)
|
|
|
|
|
video->update(deltaTime);
|
|
|
|
|
|
2022-02-19 21:46:52 +00:00
|
|
|
for (auto& anim : mSystemElements[mCarousel->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->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)
|
|
|
|
|
{
|
|
|
|
|
if (mCarousel->getNumEntries() == 0)
|
|
|
|
|
return; // Nothing to render.
|
|
|
|
|
|
2022-02-19 20:22:46 +00:00
|
|
|
bool fade {false};
|
|
|
|
|
|
|
|
|
|
if (mNavigated && mCarousel->isAnimationPlaying(0) &&
|
|
|
|
|
Settings::getInstance()->getString("TransitionStyle") == "fade")
|
|
|
|
|
fade = true;
|
|
|
|
|
|
|
|
|
|
if (!fade)
|
|
|
|
|
renderElements(parentTrans, false);
|
2022-02-06 13:55:48 +00:00
|
|
|
glm::mat4 trans {getTransform() * parentTrans};
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
// During fade transitions draw a black rectangle above all elements placed below the carousel.
|
|
|
|
|
if (mFadeOpacity != 0.0f) {
|
|
|
|
|
unsigned int fadeColor {0x00000000 | static_cast<unsigned int>(mFadeOpacity * 255.0f)};
|
|
|
|
|
Renderer::setMatrix(trans);
|
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, mSize.x, mSize.y, fadeColor, fadeColor);
|
2022-02-06 13:55:48 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
mCarousel->render(trans);
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
// For legacy themes the carousel is always rendered on top of all other elements.
|
|
|
|
|
if (!mLegacyMode)
|
|
|
|
|
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;
|
|
|
|
|
if (mCarousel->getType() == CarouselComponent::VERTICAL ||
|
|
|
|
|
mCarousel->getType() == CarouselComponent::VERTICAL_WHEEL)
|
|
|
|
|
prompts.push_back(HelpPrompt("up/down", "choose"));
|
|
|
|
|
else
|
|
|
|
|
prompts.push_back(HelpPrompt("left/right", "choose"));
|
|
|
|
|
|
|
|
|
|
prompts.push_back(HelpPrompt("a", "select"));
|
|
|
|
|
|
|
|
|
|
if (Settings::getInstance()->getBool("RandomAddButton"))
|
|
|
|
|
prompts.push_back(HelpPrompt("thumbstickclick", "random"));
|
|
|
|
|
|
|
|
|
|
if (!UIModeController::getInstance()->isUIModeKid() &&
|
|
|
|
|
Settings::getInstance()->getBool("ScreensaverControls"))
|
|
|
|
|
prompts.push_back(HelpPrompt("back", "screensaver"));
|
|
|
|
|
|
|
|
|
|
return prompts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HelpStyle SystemView::getHelpStyle()
|
|
|
|
|
{
|
|
|
|
|
HelpStyle style;
|
|
|
|
|
style.applyTheme(mCarousel->getEntry(mCarousel->getCursor()).object->getTheme(), "system");
|
|
|
|
|
return style;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-17 14:58:52 +00:00
|
|
|
void SystemView::onCursorChanged(const CursorState& /*state*/)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2022-02-14 18:32:07 +00:00
|
|
|
int cursor {mCarousel->getCursor()};
|
|
|
|
|
|
|
|
|
|
for (auto& selector : mSystemElements[cursor].gameSelectors) {
|
|
|
|
|
if (selector->getGameSelection() == GameSelectorComponent::GameSelection::RANDOM)
|
|
|
|
|
selector->setNeedsRefresh();
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-19 20:22:46 +00:00
|
|
|
for (auto& video : mSystemElements[cursor].videoComponents)
|
|
|
|
|
video->setStaticVideo();
|
|
|
|
|
|
2022-02-19 21:46:52 +00:00
|
|
|
for (auto& anim : mSystemElements[mCarousel->getCursor()].lottieAnimComponents)
|
|
|
|
|
anim->resetFileAnimation();
|
|
|
|
|
|
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-02-06 13:01:40 +00:00
|
|
|
int scrollVelocity {mCarousel->getScrollingVelocity()};
|
|
|
|
|
float startPos {mCamOffset};
|
|
|
|
|
float posMax {static_cast<float>(mCarousel->getNumEntries())};
|
2022-02-14 18:32:07 +00:00
|
|
|
float target {static_cast<float>(cursor)};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-11-15 10:30:43 +00:00
|
|
|
// Find the shortest path to the target.
|
2022-02-06 13:01:40 +00:00
|
|
|
float endPos {target}; // Directly.
|
|
|
|
|
float dist {fabs(endPos - startPos)};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2022-02-06 13:01:40 +00:00
|
|
|
if (fabs(target + posMax - startPos - scrollVelocity) < dist)
|
2020-06-21 12:25:28 +00:00
|
|
|
endPos = target + posMax; // Loop around the end (0 -> max).
|
2022-02-06 13:01:40 +00:00
|
|
|
if (fabs(target - posMax - startPos - scrollVelocity) < dist)
|
2020-06-21 12:25:28 +00:00
|
|
|
endPos = target - posMax; // Loop around the start (max - 1 -> -1).
|
|
|
|
|
|
2022-02-08 23:05:06 +00:00
|
|
|
// Make sure transitions do not animate in reverse.
|
|
|
|
|
bool changedDirection {false};
|
|
|
|
|
if (mPreviousScrollVelocity != 0 && mPreviousScrollVelocity != scrollVelocity)
|
|
|
|
|
changedDirection = true;
|
|
|
|
|
|
|
|
|
|
if (!changedDirection && scrollVelocity > 0 && endPos < startPos)
|
|
|
|
|
endPos = endPos + posMax;
|
|
|
|
|
|
|
|
|
|
if (!changedDirection && scrollVelocity < 0 && endPos > startPos)
|
|
|
|
|
endPos = endPos - posMax;
|
|
|
|
|
|
|
|
|
|
if (scrollVelocity != 0)
|
|
|
|
|
mPreviousScrollVelocity = scrollVelocity;
|
|
|
|
|
|
2022-02-06 13:01:40 +00:00
|
|
|
std::string transition_style {Settings::getInstance()->getString("TransitionStyle")};
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
|
Animation* anim;
|
2020-11-15 19:06:33 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (transition_style == "fade") {
|
2022-02-06 13:01:40 +00:00
|
|
|
float startFade {mFadeOpacity};
|
2020-06-21 12:25:28 +00:00
|
|
|
anim = new LambdaAnimation(
|
2022-02-06 13:01:40 +00:00
|
|
|
[this, startFade, startPos, endPos, posMax](float t) {
|
2021-07-07 18:03:42 +00:00
|
|
|
t -= 1;
|
2022-02-09 17:22:06 +00:00
|
|
|
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
|
|
|
|
if (f < 0.0f)
|
2021-07-07 18:03:42 +00:00
|
|
|
f += posMax;
|
|
|
|
|
if (f >= posMax)
|
|
|
|
|
f -= posMax;
|
|
|
|
|
|
|
|
|
|
t += 1;
|
2022-02-09 17:22:06 +00:00
|
|
|
|
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
|
|
|
|
2021-07-07 18:03:42 +00:00
|
|
|
// Update the game count when the entire animation has been completed.
|
2022-02-06 13:01:40 +00:00
|
|
|
if (mFadeOpacity == 1.0f)
|
2021-07-07 18:03:42 +00:00
|
|
|
updateGameCount();
|
|
|
|
|
},
|
|
|
|
|
500);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
else if (transition_style == "slide") {
|
2020-11-15 19:06:33 +00:00
|
|
|
mUpdatedGameCount = false;
|
2020-06-21 12:25:28 +00:00
|
|
|
anim = new LambdaAnimation(
|
2021-07-07 18:03:42 +00:00
|
|
|
[this, startPos, endPos, posMax](float t) {
|
|
|
|
|
t -= 1;
|
2022-02-09 17:22:06 +00:00
|
|
|
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
|
|
|
|
if (f < 0.0f)
|
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
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
500);
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
// Instant.
|
2020-11-15 19:06:33 +00:00
|
|
|
updateGameCount();
|
2020-06-21 12:25:28 +00:00
|
|
|
anim = new LambdaAnimation(
|
2021-07-07 18:03:42 +00:00
|
|
|
[this, startPos, endPos, posMax](float t) {
|
|
|
|
|
t -= 1;
|
2022-02-09 17:22:06 +00:00
|
|
|
float f {glm::mix(startPos, endPos, t * t * t + 1.0f)};
|
|
|
|
|
if (f < 0.0f)
|
2021-07-07 18:03:42 +00:00
|
|
|
f += posMax;
|
|
|
|
|
if (f >= posMax)
|
|
|
|
|
f -= posMax;
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
mCamOffset = endPos;
|
2021-07-07 18:03:42 +00:00
|
|
|
},
|
|
|
|
|
500);
|
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-02-09 17:22:06 +00:00
|
|
|
LOG(LogDebug) << "SystemView::populate(): Populating carousel";
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
auto themeSets = ThemeData::getThemeSets();
|
2022-02-07 20:05:56 +00:00
|
|
|
std::map<std::string, ThemeData::ThemeSet>::const_iterator selectedSet {
|
|
|
|
|
themeSets.find(Settings::getInstance()->getString("ThemeSet"))};
|
2017-05-27 07:40:18 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
assert(selectedSet != themeSets.cend());
|
|
|
|
|
mLegacyMode = selectedSet->second.capabilities.legacyTheme;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
if (mLegacyMode) {
|
|
|
|
|
mLegacySystemInfo = std::make_unique<TextComponent>(
|
|
|
|
|
"SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
for (auto it : SystemData::sSystemVector) {
|
|
|
|
|
const std::shared_ptr<ThemeData>& theme {it->getTheme()};
|
2022-02-07 20:05:56 +00:00
|
|
|
std::string logoPath;
|
|
|
|
|
std::string defaultLogoPath;
|
2021-10-23 15:34:20 +00:00
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
if (mLegacyMode && mViewNeedsReload)
|
|
|
|
|
legacyApplyTheme(theme);
|
2021-10-23 15:34:20 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
if (mLegacyMode) {
|
|
|
|
|
SystemViewElements elements;
|
|
|
|
|
elements.name = it->getName();
|
|
|
|
|
elements.legacyExtras = ThemeData::makeExtras(theme, "system");
|
2022-02-06 13:01:40 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
// Sort the extras by z-index.
|
|
|
|
|
std::stable_sort(
|
|
|
|
|
elements.legacyExtras.begin(), elements.legacyExtras.end(),
|
|
|
|
|
[](GuiComponent* a, GuiComponent* b) { return b->getZIndex() > a->getZIndex(); });
|
2017-04-22 14:15:16 +00:00
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
mSystemElements.emplace_back(std::move(elements));
|
2022-02-06 13:55:48 +00:00
|
|
|
}
|
2022-02-06 13:01:40 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
if (!mLegacyMode) {
|
|
|
|
|
SystemViewElements elements;
|
|
|
|
|
if (theme->hasView("system")) {
|
|
|
|
|
elements.name = it->getName();
|
2022-02-06 19:13:53 +00:00
|
|
|
elements.fullName = it->getFullName();
|
2022-02-06 13:55:48 +00:00
|
|
|
for (auto& element : theme->getViewElements("system").elements) {
|
2022-02-13 21:30:03 +00:00
|
|
|
if (element.second.type == "gameselector") {
|
2022-02-14 18:32:07 +00:00
|
|
|
elements.gameSelectors.emplace_back(
|
|
|
|
|
std::make_unique<GameSelectorComponent>(it));
|
|
|
|
|
elements.gameSelectors.back()->applyTheme(theme, "system", element.first,
|
|
|
|
|
ThemeFlags::ALL);
|
|
|
|
|
elements.gameSelectors.back()->setNeedsRefresh();
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
2022-02-06 19:36:06 +00:00
|
|
|
if (element.second.type == "carousel") {
|
|
|
|
|
mCarousel->applyTheme(theme, "system", element.first, ThemeFlags::ALL);
|
2022-02-07 20:05:56 +00:00
|
|
|
if (element.second.has("logo"))
|
|
|
|
|
logoPath = element.second.get<std::string>("logo");
|
|
|
|
|
if (element.second.has("defaultLogo"))
|
|
|
|
|
defaultLogoPath = element.second.get<std::string>("defaultLogo");
|
2022-02-06 19:36:06 +00:00
|
|
|
}
|
|
|
|
|
else if (element.second.type == "image") {
|
2022-02-06 13:55:48 +00:00
|
|
|
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());
|
2022-02-06 13:01:40 +00:00
|
|
|
}
|
2022-02-19 20:22:46 +00:00
|
|
|
else if (element.second.type == "video") {
|
|
|
|
|
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());
|
|
|
|
|
}
|
2022-02-19 21:46:52 +00:00
|
|
|
else if (element.second.type == "animation") {
|
|
|
|
|
elements.lottieAnimComponents.emplace_back(
|
|
|
|
|
std::make_unique<LottieComponent>());
|
|
|
|
|
elements.lottieAnimComponents.back()->setDefaultZIndex(35.0f);
|
|
|
|
|
elements.lottieAnimComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(elements.lottieAnimComponents.back().get());
|
|
|
|
|
}
|
2022-02-06 13:55:48 +00:00
|
|
|
else if (element.second.type == "text") {
|
2022-02-13 10:45:06 +00:00
|
|
|
if (element.second.has("systemdata") &&
|
|
|
|
|
element.second.get<std::string>("systemdata").substr(0, 9) ==
|
|
|
|
|
"gamecount") {
|
|
|
|
|
if (element.second.has("systemdata")) {
|
2022-02-06 19:13:53 +00:00
|
|
|
elements.gameCountComponents.emplace_back(
|
|
|
|
|
std::make_unique<TextComponent>());
|
|
|
|
|
elements.gameCountComponents.back()->setDefaultZIndex(40.0f);
|
|
|
|
|
elements.gameCountComponents.back()->applyTheme(
|
|
|
|
|
theme, "system", element.first, ThemeFlags::ALL);
|
|
|
|
|
elements.children.emplace_back(
|
|
|
|
|
elements.gameCountComponents.back().get());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
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 13:01:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-02-09 17:22:06 +00:00
|
|
|
else {
|
|
|
|
|
// Apply default carousel configuration.
|
|
|
|
|
mCarousel->applyTheme(theme, "system", "", ThemeFlags::ALL);
|
|
|
|
|
}
|
2021-10-15 20:54:04 +00:00
|
|
|
|
2022-02-06 13:55:48 +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();
|
|
|
|
|
});
|
2022-02-06 19:13:53 +00:00
|
|
|
mSystemElements.emplace_back(std::move(elements));
|
2022-02-06 13:01:40 +00:00
|
|
|
}
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
CarouselComponent::Entry entry;
|
|
|
|
|
entry.name = it->getName();
|
|
|
|
|
entry.object = it;
|
2022-02-07 20:05:56 +00:00
|
|
|
entry.data.logoPath = logoPath;
|
|
|
|
|
entry.data.defaultLogoPath = defaultLogoPath;
|
2022-02-06 13:55:48 +00:00
|
|
|
|
2022-02-07 20:05:56 +00:00
|
|
|
mCarousel->addEntry(theme, entry, mLegacyMode);
|
2021-10-23 15:34:20 +00:00
|
|
|
}
|
2022-02-06 13:01:40 +00:00
|
|
|
|
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() != "") {
|
|
|
|
|
if (text->getThemeSystemdata() == "name")
|
2022-02-06 19:13:53 +00:00
|
|
|
text->setValue(elements.name);
|
2022-02-13 10:45:06 +00:00
|
|
|
else if (text->getThemeSystemdata() == "fullname")
|
2022-02-06 19:13:53 +00:00
|
|
|
text->setValue(elements.fullName);
|
|
|
|
|
else
|
2022-02-13 10:45:06 +00:00
|
|
|
text->setValue(text->getThemeSystemdata());
|
2022-02-06 19:13:53 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
if (mCarousel->getNumEntries() == 0) {
|
|
|
|
|
// Something is wrong, there is not a single system to show, check if UI mode is not full.
|
|
|
|
|
if (!UIModeController::getInstance()->isUIModeFull()) {
|
|
|
|
|
Settings::getInstance()->setString("UIMode", "full");
|
|
|
|
|
mWindow->pushGui(new GuiMsgBox(
|
|
|
|
|
getHelpStyle(),
|
|
|
|
|
"The selected UI mode has nothing to show,\n returning to UI mode \"Full\"", "OK",
|
|
|
|
|
nullptr));
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-03-13 21:11:07 +00:00
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-06 13:55:48 +00:00
|
|
|
void SystemView::updateGameCount()
|
2017-03-13 21:11:07 +00:00
|
|
|
{
|
2022-02-06 13:55:48 +00:00
|
|
|
std::pair<unsigned int, unsigned int> gameCount =
|
|
|
|
|
mCarousel->getSelected()->getDisplayedGameCount();
|
|
|
|
|
std::stringstream ss;
|
2022-02-06 19:13:53 +00:00
|
|
|
std::stringstream ssGames;
|
|
|
|
|
std::stringstream ssFavorites;
|
|
|
|
|
bool games {false};
|
|
|
|
|
bool favorites {false};
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
if (!mCarousel->getSelected()->isGameSystem()) {
|
|
|
|
|
ss << "Configuration";
|
|
|
|
|
}
|
2022-02-06 13:55:48 +00:00
|
|
|
else if (mCarousel->getSelected()->isCollection() &&
|
2022-02-06 19:13:53 +00:00
|
|
|
(mCarousel->getSelected()->getName() == "favorites")) {
|
|
|
|
|
ss << gameCount.first << " Game" << (gameCount.first == 1 ? " " : "s");
|
|
|
|
|
}
|
2022-02-06 13:55:48 +00:00
|
|
|
else if (mCarousel->getSelected()->isCollection() &&
|
2022-02-06 19:13:53 +00:00
|
|
|
(mCarousel->getSelected()->getName() == "recent")) {
|
|
|
|
|
// The "recent" gamelist has probably been trimmed after sorting, so we'll cap it at
|
|
|
|
|
// its maximum limit of 50 games.
|
|
|
|
|
ss << (gameCount.first > 50 ? 50 : gameCount.first) << " Game"
|
|
|
|
|
<< (gameCount.first == 1 ? " " : "s");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
ss << gameCount.first << " Game" << (gameCount.first == 1 ? " " : "s ") << "("
|
|
|
|
|
<< gameCount.second << " Favorite" << (gameCount.second == 1 ? ")" : "s)");
|
|
|
|
|
ssGames << gameCount.first << " Game" << (gameCount.first == 1 ? " " : "s ");
|
|
|
|
|
ssFavorites << gameCount.second << " Favorite" << (gameCount.second == 1 ? "" : "s");
|
|
|
|
|
games = true;
|
|
|
|
|
favorites = true;
|
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2022-02-06 19:13:53 +00:00
|
|
|
if (mLegacyMode) {
|
|
|
|
|
mLegacySystemInfo->setText(ss.str());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (auto& gameCount : mSystemElements[mCarousel->getCursor()].gameCountComponents) {
|
2022-02-13 10:45:06 +00:00
|
|
|
if (gameCount->getThemeSystemdata() == "gamecount") {
|
2022-02-06 19:13:53 +00:00
|
|
|
gameCount->setValue(ss.str());
|
|
|
|
|
}
|
2022-02-13 10:45:06 +00:00
|
|
|
else if (gameCount->getThemeSystemdata() == "gamecount_games") {
|
2022-02-06 19:13:53 +00:00
|
|
|
if (games)
|
|
|
|
|
gameCount->setValue(ssGames.str());
|
|
|
|
|
else
|
|
|
|
|
gameCount->setValue(ss.str());
|
|
|
|
|
}
|
2022-02-13 10:45:06 +00:00
|
|
|
else if (gameCount->getThemeSystemdata() == "gamecount_favorites") {
|
2022-02-06 19:13:53 +00:00
|
|
|
gameCount->setValue(ssFavorites.str());
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-02-13 10:45:06 +00:00
|
|
|
gameCount->setValue(gameCount->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-02-14 18:32:07 +00:00
|
|
|
if (mLegacyMode)
|
|
|
|
|
return;
|
|
|
|
|
|
2022-02-13 21:30:03 +00:00
|
|
|
int cursor {mCarousel->getCursor()};
|
|
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
if (mSystemElements[cursor].gameSelectors.size() == 0)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
bool multipleSelectors {mSystemElements[cursor].gameSelectors.size() > 1};
|
|
|
|
|
|
|
|
|
|
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) {
|
|
|
|
|
if (selector->getSelectorName() == imageSelector)
|
|
|
|
|
gameSelector = selector.get();
|
|
|
|
|
}
|
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();
|
|
|
|
|
}
|
|
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
2022-02-13 21:30:03 +00:00
|
|
|
if (!games.empty()) {
|
2022-02-14 18:32:07 +00:00
|
|
|
std::string path;
|
|
|
|
|
for (auto& imageType : image->getThemeImageTypes()) {
|
|
|
|
|
if (imageType == "image") {
|
|
|
|
|
path = games.front()->getImagePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "miximage") {
|
|
|
|
|
path = games.front()->getMiximagePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "marquee") {
|
|
|
|
|
path = games.front()->getMarqueePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "screenshot") {
|
|
|
|
|
path = games.front()->getScreenshotPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "titlescreen") {
|
|
|
|
|
path = games.front()->getTitleScreenPath();
|
|
|
|
|
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") {
|
|
|
|
|
path = games.front()->getCoverPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "backcover") {
|
|
|
|
|
path = games.front()->getBackCoverPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "3dbox") {
|
|
|
|
|
path = games.front()->get3DBoxPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "fanart") {
|
|
|
|
|
path = games.front()->getFanArtPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
image->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "thumbnail") {
|
|
|
|
|
path = games.front()->getThumbnailPath();
|
|
|
|
|
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.
|
|
|
|
|
if (video->hasStaticVideo())
|
|
|
|
|
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) {
|
|
|
|
|
if (selector->getSelectorName() == videoSelector)
|
|
|
|
|
gameSelector = selector.get();
|
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
|
|
|
|
if (!games.empty()) {
|
|
|
|
|
if (!video->setVideo(games.front()->getVideoPath()))
|
|
|
|
|
video->setDefaultVideo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& video : mSystemElements[cursor].videoComponents) {
|
|
|
|
|
if (video->getThemeImageTypes().size() == 0)
|
|
|
|
|
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) {
|
|
|
|
|
if (selector->getSelectorName() == imageSelector)
|
|
|
|
|
gameSelector = selector.get();
|
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
|
|
|
|
if (!games.empty()) {
|
|
|
|
|
std::string path;
|
|
|
|
|
for (auto& imageType : video->getThemeImageTypes()) {
|
|
|
|
|
if (imageType == "image") {
|
|
|
|
|
path = games.front()->getImagePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "miximage") {
|
|
|
|
|
path = games.front()->getMiximagePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "marquee") {
|
|
|
|
|
path = games.front()->getMarqueePath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "screenshot") {
|
|
|
|
|
path = games.front()->getScreenshotPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "titlescreen") {
|
|
|
|
|
path = games.front()->getTitleScreenPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "cover") {
|
|
|
|
|
path = games.front()->getCoverPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "backcover") {
|
|
|
|
|
path = games.front()->getBackCoverPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "3dbox") {
|
|
|
|
|
path = games.front()->get3DBoxPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "fanart") {
|
|
|
|
|
path = games.front()->getFanArtPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (imageType == "thumbnail") {
|
|
|
|
|
path = games.front()->getThumbnailPath();
|
|
|
|
|
if (path != "") {
|
|
|
|
|
video->setImage(path);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// This is needed so the default image is set if no game media was found.
|
|
|
|
|
if (path == "" && video->getThemeImageTypes().size() > 0)
|
|
|
|
|
video->setImage("");
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
video->setImage("");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-14 18:32:07 +00:00
|
|
|
for (auto& text : mSystemElements[cursor].textComponents) {
|
|
|
|
|
if (text->getThemeMetadata() == "")
|
|
|
|
|
continue;
|
|
|
|
|
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 "
|
|
|
|
|
"use, so 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) {
|
|
|
|
|
if (selector->getSelectorName() == textSelector)
|
|
|
|
|
gameSelector = selector.get();
|
|
|
|
|
}
|
|
|
|
|
if (gameSelector == nullptr)
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
gameSelector = mSystemElements[cursor].gameSelectors.front().get();
|
|
|
|
|
}
|
|
|
|
|
gameSelector->refreshGames();
|
|
|
|
|
std::vector<FileData*> games {gameSelector->getGames()};
|
|
|
|
|
if (!games.empty()) {
|
|
|
|
|
const std::string metadata {text->getThemeMetadata()};
|
|
|
|
|
if (metadata == "name")
|
|
|
|
|
text->setValue(games.front()->metadata.get("name"));
|
|
|
|
|
if (metadata == "description")
|
|
|
|
|
text->setValue(games.front()->metadata.get("desc"));
|
|
|
|
|
if (metadata == "developer")
|
|
|
|
|
text->setValue(games.front()->metadata.get("developer"));
|
|
|
|
|
if (metadata == "publisher")
|
|
|
|
|
text->setValue(games.front()->metadata.get("publisher"));
|
|
|
|
|
if (metadata == "genre")
|
|
|
|
|
text->setValue(games.front()->metadata.get("genre"));
|
|
|
|
|
if (metadata == "players")
|
|
|
|
|
text->setValue(games.front()->metadata.get("players"));
|
|
|
|
|
if (metadata == "favorite")
|
|
|
|
|
text->setValue(games.front()->metadata.get("favorite") == "true" ? "yes" : "no");
|
|
|
|
|
if (metadata == "completed")
|
|
|
|
|
text->setValue(games.front()->metadata.get("completed") == "true" ? "yes" : "no");
|
|
|
|
|
if (metadata == "kidgame")
|
|
|
|
|
text->setValue(games.front()->metadata.get("kidgame") == "true" ? "yes" : "no");
|
|
|
|
|
if (metadata == "broken")
|
|
|
|
|
text->setValue(games.front()->metadata.get("broken") == "true" ? "yes" : "no");
|
|
|
|
|
if (metadata == "playcount")
|
|
|
|
|
text->setValue(games.front()->metadata.get("playcount"));
|
|
|
|
|
if (metadata == "altemulator")
|
|
|
|
|
text->setValue(games.front()->metadata.get("altemulator"));
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
text->setValue("");
|
2022-02-13 21:30:03 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
void SystemView::legacyApplyTheme(const std::shared_ptr<ThemeData>& theme)
|
2017-03-13 21:11:07 +00:00
|
|
|
{
|
2022-02-06 13:01:40 +00:00
|
|
|
if (theme->hasView("system"))
|
|
|
|
|
mViewNeedsReload = false;
|
|
|
|
|
else
|
|
|
|
|
mViewNeedsReload = true;
|
2021-07-07 18:03:42 +00:00
|
|
|
|
2022-02-09 17:22:06 +00:00
|
|
|
mCarousel->applyTheme(theme, "system", "carousel_systemcarousel", ThemeFlags::ALL);
|
|
|
|
|
|
|
|
|
|
mLegacySystemInfo->setSize(mSize.x, mLegacySystemInfo->getFont()->getLetterHeight() * 2.2f);
|
|
|
|
|
mLegacySystemInfo->setPosition(0.0f, mCarousel->getPosition().y + mCarousel->getSize().y);
|
|
|
|
|
mLegacySystemInfo->setBackgroundColor(0xDDDDDDD8);
|
|
|
|
|
mLegacySystemInfo->setRenderBackground(true);
|
|
|
|
|
mLegacySystemInfo->setFont(
|
|
|
|
|
Font::get(static_cast<int>(0.035f * mSize.y), Font::getDefaultPath()));
|
|
|
|
|
mLegacySystemInfo->setColor(0x000000FF);
|
|
|
|
|
mLegacySystemInfo->setUppercase(true);
|
|
|
|
|
mLegacySystemInfo->setZIndex(49.0f);
|
|
|
|
|
mLegacySystemInfo->setDefaultZIndex(49.0f);
|
|
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* sysInfoElem {
|
|
|
|
|
theme->getElement("system", "text_systemInfo", "text")};
|
|
|
|
|
|
|
|
|
|
if (sysInfoElem)
|
|
|
|
|
mLegacySystemInfo->applyTheme(theme, "system", "text_systemInfo", ThemeFlags::ALL);
|
2017-04-22 14:15:16 +00:00
|
|
|
}
|
2017-03-13 21:11:07 +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};
|
|
|
|
|
|
|
|
|
|
// Adding texture loading buffers depending on scrolling speed and status.
|
|
|
|
|
int bufferIndex {mCarousel->getScrollingVelocity() + 1};
|
|
|
|
|
|
|
|
|
|
const float primaryZIndex {mCarousel->getZIndex()};
|
|
|
|
|
|
|
|
|
|
for (int i = static_cast<int>(mCamOffset) + logoBuffersLeft[bufferIndex];
|
|
|
|
|
i <= static_cast<int>(mCamOffset) + logoBuffersRight[bufferIndex]; ++i) {
|
|
|
|
|
int index {i};
|
|
|
|
|
while (index < 0)
|
|
|
|
|
index += static_cast<int>(mCarousel->getNumEntries());
|
|
|
|
|
while (index >= static_cast<int>(mCarousel->getNumEntries()))
|
|
|
|
|
index -= static_cast<int>(mCarousel->getNumEntries());
|
|
|
|
|
|
|
|
|
|
if (mCarousel->isAnimationPlaying(0) || index == mCarousel->getCursor()) {
|
|
|
|
|
glm::mat4 elementTrans {trans};
|
|
|
|
|
if (mCarousel->getType() == CarouselComponent::HORIZONTAL ||
|
|
|
|
|
mCarousel->getType() == CarouselComponent::HORIZONTAL_WHEEL)
|
|
|
|
|
elementTrans = glm::translate(elementTrans,
|
|
|
|
|
glm::vec3 {(i - mCamOffset) * mSize.x, 0.0f, 0.0f});
|
|
|
|
|
else
|
|
|
|
|
elementTrans = glm::translate(elementTrans,
|
|
|
|
|
glm::vec3 {0.0f, (i - mCamOffset) * mSize.y, 0.0f});
|
|
|
|
|
|
|
|
|
|
Renderer::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)});
|
|
|
|
|
|
|
|
|
|
if (mLegacyMode && mSystemElements.size() > static_cast<size_t>(index)) {
|
|
|
|
|
for (auto element : mSystemElements[index].legacyExtras)
|
|
|
|
|
element->render(elementTrans);
|
|
|
|
|
}
|
|
|
|
|
else if (!mLegacyMode && mSystemElements.size() > static_cast<size_t>(index)) {
|
|
|
|
|
for (auto child : mSystemElements[index].children) {
|
|
|
|
|
if (abovePrimary && child->getZIndex() > primaryZIndex && mFadeOpacity == 0.0f)
|
|
|
|
|
child->render(elementTrans);
|
|
|
|
|
else if (!abovePrimary && child->getZIndex() <= primaryZIndex)
|
|
|
|
|
child->render(elementTrans);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (mLegacyMode)
|
|
|
|
|
mLegacySystemInfo->render(elementTrans);
|
|
|
|
|
|
|
|
|
|
Renderer::popClipRect();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|