2020-09-15 20:57:54 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-09-15 20:57:54 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-21 12:25:28 +00:00
|
|
|
// ViewController.cpp
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
2020-06-21 12:25:28 +00:00
|
|
|
// Handles overall system navigation including animations and transitions.
|
|
|
|
// Also creates the gamelist views and handles refresh and reloads of these when needed
|
|
|
|
// (for example when metadata has been changed or when a list sorting has taken place).
|
|
|
|
// Initiates the launching of games, calling FileData to do the actual launch.
|
2020-05-24 08:29:29 +00:00
|
|
|
//
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "views/ViewController.h"
|
|
|
|
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "animations/Animation.h"
|
|
|
|
#include "animations/LambdaAnimation.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
#include "animations/MoveCameraAnimation.h"
|
2020-08-15 13:41:11 +00:00
|
|
|
#include "guis/GuiInfoPopup.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "guis/GuiMenu.h"
|
2020-07-14 17:16:21 +00:00
|
|
|
#include "guis/GuiMsgBox.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "views/gamelist/DetailedGameListView.h"
|
2018-03-22 07:03:12 +00:00
|
|
|
#include "views/gamelist/GridGameListView.h"
|
2020-09-15 20:57:54 +00:00
|
|
|
#include "views/gamelist/IGameListView.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "views/gamelist/VideoGameListView.h"
|
|
|
|
#include "views/SystemView.h"
|
2017-11-18 22:23:56 +00:00
|
|
|
#include "views/UIModeController.h"
|
2017-09-08 13:20:07 +00:00
|
|
|
#include "FileFilterIndex.h"
|
2020-07-14 17:16:21 +00:00
|
|
|
#include "InputManager.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "Log.h"
|
|
|
|
#include "Settings.h"
|
2020-07-14 17:16:21 +00:00
|
|
|
#include "Sound.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "SystemData.h"
|
|
|
|
#include "Window.h"
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
ViewController* ViewController::sInstance = nullptr;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
|
|
|
ViewController* ViewController::get()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(sInstance);
|
|
|
|
return sInstance;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::init(Window* window)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(!sInstance);
|
|
|
|
sInstance = new ViewController(window);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2020-05-24 08:29:29 +00:00
|
|
|
ViewController::ViewController(
|
2020-06-21 12:25:28 +00:00
|
|
|
Window* window)
|
|
|
|
: GuiComponent(window),
|
|
|
|
mCurrentView(nullptr),
|
|
|
|
mCamera(Transform4x4f::Identity()),
|
|
|
|
mFadeOpacity(0),
|
|
|
|
mLockInput(false)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mState.viewing = NOTHING;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ViewController::~ViewController()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(sInstance == this);
|
|
|
|
sInstance = nullptr;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::goToStart()
|
|
|
|
{
|
2020-07-14 17:16:21 +00:00
|
|
|
// Check if the keyboard config is set as application default, meaning no user
|
|
|
|
// configuration has been performed.
|
|
|
|
if (InputManager::getInstance()->
|
|
|
|
getInputConfigByDevice(DEVICE_KEYBOARD)->getDefaultConfigFlag()) {
|
2020-08-03 09:39:04 +00:00
|
|
|
|
|
|
|
LOG(LogInfo) << "Applying default keyboard mappings.";
|
|
|
|
|
2020-07-14 17:16:21 +00:00
|
|
|
if (Settings::getInstance()->getBool("ShowDefaultKeyboardWarning")) {
|
|
|
|
std::string message = "NO KEYBOARD CONFIGURATION COULD BE\n"
|
|
|
|
"FOUND IN ES_INPUT.CFG, SO APPLYING THE\n"
|
|
|
|
"DEFAULT KEYBOARD MAPPINGS. IT'S HOWEVER\n"
|
|
|
|
"RECOMMENDED TO SETUP YOUR OWN KEYBOARD\n"
|
|
|
|
"CONFIGURATION. TO DO SO, CHOOSE THE ENTRY\n"
|
|
|
|
"\"CONFIGURE INPUT\" ON THE MAIN MENU.";
|
|
|
|
|
|
|
|
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), message.c_str(),
|
|
|
|
"OK", nullptr, "DON'T SHOW AGAIN", [] {
|
|
|
|
Settings::getInstance()->setBool("ShowDefaultKeyboardWarning", false);
|
|
|
|
Settings::getInstance()->saveFile();
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// If a specific system is requested, go directly to its game list.
|
|
|
|
auto requestedSystem = Settings::getInstance()->getString("StartupSystem");
|
|
|
|
if ("" != requestedSystem && "retropie" != requestedSystem) {
|
|
|
|
for (auto it = SystemData::sSystemVector.cbegin();
|
|
|
|
it != SystemData::sSystemVector.cend(); it++) {
|
|
|
|
if ((*it)->getName() == requestedSystem) {
|
|
|
|
goToGameList(*it);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Requested system doesn't exist.
|
|
|
|
Settings::getInstance()->setString("StartupSystem", "");
|
|
|
|
}
|
2020-08-24 16:51:55 +00:00
|
|
|
// Get the first system entry as sorted by full system names in SystemView.
|
|
|
|
goToSystemView(getSystemListView()->getFirst());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-11-18 22:23:56 +00:00
|
|
|
void ViewController::ReloadAndGoToStart()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
mWindow->renderLoadingScreen("Loading...");
|
|
|
|
ViewController::get()->reloadAll();
|
|
|
|
ViewController::get()->goToStart();
|
2017-11-18 22:23:56 +00:00
|
|
|
}
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
bool ViewController::isCameraMoving()
|
|
|
|
{
|
|
|
|
if (mCurrentView) {
|
|
|
|
if (mCamera.r3().x() != -mCurrentView->getPosition().x() ||
|
|
|
|
mCamera.r3().y() != -mCurrentView->getPosition().y())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::resetMovingCamera()
|
|
|
|
{
|
|
|
|
if (isCameraMoving()) {
|
|
|
|
mCamera.r3().x() = -mCurrentView->getPosition().x();
|
|
|
|
mCamera.r3().y() = -mCurrentView->getPosition().y();
|
|
|
|
stopAllAnimations();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::stopScrolling()
|
|
|
|
{
|
|
|
|
mSystemListView->stopScrolling();
|
|
|
|
mCurrentView->stopListScrolling();
|
|
|
|
|
|
|
|
if (mSystemListView->isAnimationPlaying(0))
|
|
|
|
mSystemListView->finishAnimation(0);
|
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
int ViewController::getSystemId(SystemData* system)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
2020-09-15 20:57:54 +00:00
|
|
|
return static_cast<int>(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::goToSystemView(SystemData* system)
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
// Tell any current view it's about to be hidden and stop its rendering.
|
|
|
|
if (mCurrentView) {
|
2020-06-21 12:25:28 +00:00
|
|
|
mCurrentView->onHide();
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->setRenderView(false);
|
|
|
|
}
|
2016-12-04 23:47:34 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
mState.viewing = SYSTEM_SELECT;
|
|
|
|
mState.system = system;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
auto systemList = getSystemListView();
|
2020-09-15 20:57:54 +00:00
|
|
|
systemList->setPosition(getSystemId(system) * static_cast<float>(Renderer::getScreenWidth()),
|
2020-06-21 12:25:28 +00:00
|
|
|
systemList->getPosition().y());
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
systemList->goToSystem(system, false);
|
|
|
|
mCurrentView = systemList;
|
|
|
|
mCurrentView->onShow();
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->setRenderView(true);
|
2020-06-21 12:25:28 +00:00
|
|
|
PowerSaver::setState(true);
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
playViewTransition();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::goToNextGameList()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(mState.viewing == GAME_LIST);
|
|
|
|
SystemData* system = getState().getSystem();
|
|
|
|
assert(system);
|
|
|
|
NavigationSounds::getInstance()->playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
|
|
|
goToGameList(system->getNext());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::goToPrevGameList()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
assert(mState.viewing == GAME_LIST);
|
|
|
|
SystemData* system = getState().getSystem();
|
|
|
|
assert(system);
|
|
|
|
NavigationSounds::getInstance()->playThemeNavigationSound(QUICKSYSSELECTSOUND);
|
|
|
|
goToGameList(system->getPrev());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::goToGameList(SystemData* system)
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
// Stop any scrolling, animations and camera movements.
|
|
|
|
if (mSystemListView) {
|
|
|
|
mSystemListView->stopScrolling();
|
|
|
|
if (mSystemListView->isAnimationPlaying(0))
|
|
|
|
mSystemListView->finishAnimation(0);
|
|
|
|
}
|
|
|
|
resetMovingCamera();
|
|
|
|
|
|
|
|
// Disable rendering of the system view.
|
|
|
|
if (getSystemListView()->getRenderView())
|
|
|
|
getSystemListView()->setRenderView(false);
|
|
|
|
|
|
|
|
// If switching between gamelists, disable rendering of the current view.
|
|
|
|
if (mCurrentView)
|
|
|
|
mCurrentView->setRenderView(false);
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mState.viewing == SYSTEM_SELECT) {
|
|
|
|
// Move system list.
|
|
|
|
auto sysList = getSystemListView();
|
|
|
|
float offX = sysList->getPosition().x();
|
|
|
|
int sysId = getSystemId(system);
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
sysList->setPosition(sysId * static_cast<float>(Renderer::getScreenWidth()),
|
2020-06-21 12:25:28 +00:00
|
|
|
sysList->getPosition().y());
|
|
|
|
offX = sysList->getPosition().x() - offX;
|
|
|
|
mCamera.translation().x() -= offX;
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
mState.viewing = GAME_LIST;
|
|
|
|
mState.system = system;
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mCurrentView)
|
|
|
|
mCurrentView->onHide();
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
mCurrentView = getGameListView(system);
|
2020-05-24 08:29:29 +00:00
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
if (mCurrentView) {
|
2020-06-21 12:25:28 +00:00
|
|
|
mCurrentView->onShow();
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->setRenderView(true);
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
playViewTransition();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::playViewTransition()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
Vector3f target(Vector3f::Zero());
|
|
|
|
if (mCurrentView)
|
|
|
|
target = mCurrentView->getPosition();
|
|
|
|
|
|
|
|
// No need to animate, we're not going anywhere (probably due to goToNextGamelist()
|
|
|
|
// or goToPrevGamelist() being called when there's only 1 system).
|
|
|
|
if (target == -mCamera.translation() && !isAnimationPlaying(0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
|
|
|
|
|
|
|
|
if (transition_style == "fade") {
|
|
|
|
// Fade.
|
|
|
|
// Stop whatever's currently playing, leaving mFadeOpacity wherever it is.
|
|
|
|
cancelAnimation(0);
|
|
|
|
|
|
|
|
auto fadeFunc = [this](float t) {
|
|
|
|
mFadeOpacity = Math::lerp(0, 1, t);
|
|
|
|
};
|
|
|
|
|
2020-07-19 10:23:30 +00:00
|
|
|
const static int FADE_DURATION = 120; // Fade in/out time.
|
|
|
|
const static int FADE_WAIT = 200; // Time to wait between in/out.
|
2020-06-21 12:25:28 +00:00
|
|
|
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, [this, fadeFunc, target] {
|
|
|
|
this->mCamera.translation() = -target;
|
|
|
|
updateHelpPrompts();
|
|
|
|
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT, nullptr, true);
|
|
|
|
});
|
|
|
|
|
|
|
|
// Fast-forward animation if we're partway faded.
|
|
|
|
if (target == -mCamera.translation()) {
|
|
|
|
// Not changing screens, so cancel the first half entirely.
|
|
|
|
advanceAnimation(0, FADE_DURATION);
|
|
|
|
advanceAnimation(0, FADE_WAIT);
|
2020-09-15 20:57:54 +00:00
|
|
|
advanceAnimation(0, FADE_DURATION - static_cast<int>(mFadeOpacity * FADE_DURATION));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
else {
|
2020-09-15 20:57:54 +00:00
|
|
|
advanceAnimation(0, static_cast<int>(mFadeOpacity * FADE_DURATION));
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (transition_style == "slide") {
|
|
|
|
// Slide or simple slide.
|
|
|
|
setAnimation(new MoveCameraAnimation(mCamera, target));
|
|
|
|
updateHelpPrompts(); // Update help prompts immediately.
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Instant.
|
|
|
|
setAnimation(new LambdaAnimation([this, target](float /*t*/) {
|
|
|
|
this->mCamera.translation() = -target; }, 1));
|
|
|
|
updateHelpPrompts();
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::onFileChanged(FileData* file, FileChangeType change)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
auto it = mGameListViews.find(file->getSystem());
|
|
|
|
if (it != mGameListViews.cend())
|
|
|
|
it->second->onFileChanged(file, change);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void ViewController::launch(FileData* game, Vector3f center)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (game->getType() != GAME) {
|
2020-09-15 20:57:54 +00:00
|
|
|
LOG(LogError) << "tried to launch something that isn't a game.";
|
2020-06-21 12:25:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// If the video view style is used, pause the video currently playing or block the
|
|
|
|
// video from starting to play if the static image is still shown.
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mCurrentView)
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->onPauseVideo();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-09-17 20:00:07 +00:00
|
|
|
// Disable text scrolling. It will be enabled again in FileData upon returning from the game.
|
|
|
|
mWindow->setAllowTextScrolling(false);
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
stopAnimation(1); // Make sure the fade in isn't still playing.
|
|
|
|
mWindow->stopInfoPopup(); // Make sure we disable any existing info popup.
|
|
|
|
mLockInput = true;
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// Until a proper game launch screen is implemented, at least this will let the
|
|
|
|
// user know that something is actually happening (in addition to the launch sound,
|
|
|
|
// if navigation sounds are enabled).
|
2020-08-15 13:41:11 +00:00
|
|
|
GuiInfoPopup* s = new GuiInfoPopup(mWindow, "LAUNCHING GAME '" +
|
|
|
|
Utils::String::toUpper(game->metadata.get("name") + "'"), 10000);
|
|
|
|
mWindow->setInfoPopup(s);
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
NavigationSounds::getInstance()->playThemeNavigationSound(LAUNCHSOUND);
|
2020-09-15 20:57:54 +00:00
|
|
|
|
|
|
|
// This is just a dummy animation in order for the launch notification popup to be
|
|
|
|
// displayed briefly, and for the navigation sound playing to be able to complete.
|
|
|
|
// During this time period, all user input is blocked.
|
|
|
|
setAnimation(new LambdaAnimation([](float t){}, 1700), 0, [this, game] {
|
|
|
|
while (NavigationSounds::getInstance()->isPlayingThemeNavigationSound(LAUNCHSOUND));
|
2020-07-19 20:08:14 +00:00
|
|
|
game->launchGame(mWindow);
|
2020-09-15 20:57:54 +00:00
|
|
|
onFileChanged(game, FILE_METADATA_CHANGED);
|
2020-07-19 20:08:14 +00:00
|
|
|
if (mCurrentView)
|
|
|
|
mCurrentView->onShow();
|
2020-09-15 20:57:54 +00:00
|
|
|
// This is a workaround so that any key or button presses used for exiting the emulator
|
|
|
|
// are not captured upon returning to ES.
|
|
|
|
setAnimation(new LambdaAnimation([](float t){}, 1), 0, [this] {
|
|
|
|
mLockInput = false;
|
|
|
|
});
|
2020-07-19 20:08:14 +00:00
|
|
|
});
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-07-18 09:45:50 +00:00
|
|
|
void ViewController::removeGameListView(SystemData* system)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
auto exists = mGameListViews.find(system);
|
|
|
|
if (exists != mGameListViews.cend()) {
|
|
|
|
exists->second.reset();
|
|
|
|
mGameListViews.erase(system);
|
|
|
|
}
|
2017-07-18 09:45:50 +00:00
|
|
|
}
|
|
|
|
|
2014-06-25 16:29:58 +00:00
|
|
|
std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* system)
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
// If we have already created an entry for this system, then return that one.
|
2020-06-21 12:25:28 +00:00
|
|
|
auto exists = mGameListViews.find(system);
|
|
|
|
if (exists != mGameListViews.cend())
|
|
|
|
return exists->second;
|
|
|
|
|
|
|
|
system->getIndex()->setUIModeFilters();
|
2020-09-15 20:57:54 +00:00
|
|
|
// If there's no entry, then create it and return it.
|
2020-06-21 12:25:28 +00:00
|
|
|
std::shared_ptr<IGameListView> view;
|
|
|
|
|
|
|
|
bool themeHasVideoView = system->getTheme()->hasView("video");
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// Decide which view style to use.
|
|
|
|
GameListViewType selectedViewStyle = AUTOMATIC;
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
std::string viewPreference = Settings::getInstance()->getString("GamelistViewStyle");
|
|
|
|
if (viewPreference.compare("basic") == 0)
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = BASIC;
|
2020-06-21 12:25:28 +00:00
|
|
|
if (viewPreference.compare("detailed") == 0)
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = DETAILED;
|
2020-06-21 12:25:28 +00:00
|
|
|
if (viewPreference.compare("grid") == 0)
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = GRID;
|
2020-06-21 12:25:28 +00:00
|
|
|
if (viewPreference.compare("video") == 0)
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = VIDEO;
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
if (selectedViewStyle == AUTOMATIC) {
|
2020-06-21 12:25:28 +00:00
|
|
|
std::vector<FileData*> files = system->getRootFolder()->getFilesRecursive(GAME | FOLDER);
|
|
|
|
for (auto it = files.cbegin(); it != files.cend(); it++) {
|
|
|
|
if (themeHasVideoView && !(*it)->getVideoPath().empty()) {
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = VIDEO;
|
2020-06-21 12:25:28 +00:00
|
|
|
break;
|
|
|
|
}
|
2020-07-13 18:10:09 +00:00
|
|
|
else if (!(*it)->getImagePath().empty()) {
|
2020-09-15 20:57:54 +00:00
|
|
|
selectedViewStyle = DETAILED;
|
2020-06-21 12:25:28 +00:00
|
|
|
// Don't break out in case any subsequent files have videos.
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the view.
|
2020-09-15 20:57:54 +00:00
|
|
|
switch (selectedViewStyle)
|
2020-06-21 12:25:28 +00:00
|
|
|
{
|
|
|
|
case VIDEO:
|
|
|
|
view = std::shared_ptr<IGameListView>(
|
|
|
|
new VideoGameListView(mWindow, system->getRootFolder()));
|
|
|
|
break;
|
|
|
|
case DETAILED:
|
|
|
|
view = std::shared_ptr<IGameListView>(
|
|
|
|
new DetailedGameListView(mWindow, system->getRootFolder()));
|
|
|
|
break;
|
|
|
|
case GRID:
|
|
|
|
view = std::shared_ptr<IGameListView>(
|
|
|
|
new GridGameListView(mWindow, system->getRootFolder()));
|
|
|
|
break;
|
|
|
|
case BASIC:
|
|
|
|
default:
|
|
|
|
view = std::shared_ptr<IGameListView>(
|
|
|
|
new BasicGameListView(mWindow, system->getRootFolder()));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
view->setTheme(system->getTheme());
|
|
|
|
|
|
|
|
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
2020-09-15 20:57:54 +00:00
|
|
|
int id = static_cast<int>(
|
|
|
|
std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
|
|
|
|
view->setPosition(id * static_cast<float>(Renderer::getScreenWidth()),
|
|
|
|
static_cast<float>(Renderer::getScreenHeight() * 2));
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
addChild(view.get());
|
|
|
|
|
|
|
|
mGameListViews[system] = view;
|
|
|
|
return view;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<SystemView> ViewController::getSystemListView()
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
// If we have already created a system view entry, then return it.
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mSystemListView)
|
|
|
|
return mSystemListView;
|
|
|
|
|
|
|
|
mSystemListView = std::shared_ptr<SystemView>(new SystemView(mWindow));
|
|
|
|
addChild(mSystemListView.get());
|
2020-09-15 20:57:54 +00:00
|
|
|
mSystemListView->setPosition(0, static_cast<float>(Renderer::getScreenHeight()));
|
2020-06-21 12:25:28 +00:00
|
|
|
return mSystemListView;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool ViewController::input(InputConfig* config, Input input)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mLockInput)
|
|
|
|
return true;
|
|
|
|
|
2020-08-23 15:04:30 +00:00
|
|
|
#if defined(_WIN64)
|
2020-07-19 20:08:14 +00:00
|
|
|
// This code is only needed for Windows, where we may need to keep ES running while
|
|
|
|
// the game/emulator is in use. It's basically used to pause any playing game video
|
|
|
|
// and to keep the screensaver from activating.
|
|
|
|
if (Settings::getInstance()->getBool("RunInBackground")) {
|
|
|
|
// If we have previously launched a game and there is now input registered, it means
|
|
|
|
// the user is back in ES, so unset the flag to indicate that a game has been launched
|
|
|
|
// and update all the GUI components to reflect this.
|
|
|
|
if (mWindow->getGameLaunchedState())
|
|
|
|
mWindow->unsetLaunchedGame();
|
2020-07-18 11:21:44 +00:00
|
|
|
}
|
2020-07-18 21:07:02 +00:00
|
|
|
#endif
|
2020-07-18 11:21:44 +00:00
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// Open the main menu.
|
2020-06-21 12:25:28 +00:00
|
|
|
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
2020-07-09 17:24:20 +00:00
|
|
|
!Settings::getInstance()->getBool("ShowKidStartMenu")) &&
|
2020-06-21 12:25:28 +00:00
|
|
|
config->isMappedTo("start", input) && input.value != 0) {
|
|
|
|
// If we don't stop the scrolling here, it will continue to
|
|
|
|
// run after closing the menu.
|
|
|
|
if (mSystemListView->isScrolling())
|
|
|
|
mSystemListView->stopScrolling();
|
|
|
|
// Finish the animation too, so that it doesn't continue
|
|
|
|
// to play when we've closed the menu.
|
|
|
|
if (mSystemListView->isAnimationPlaying(0))
|
|
|
|
mSystemListView->finishAnimation(0);
|
2020-09-13 17:08:17 +00:00
|
|
|
// Stop the gamelist scrolling as well as it would otherwise
|
|
|
|
// also continue to run after closing the menu.
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->stopListScrolling();
|
|
|
|
// Finally, if the camera is currently moving, reset its position.
|
|
|
|
resetMovingCamera();
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
mWindow->pushGui(new GuiMenu(mWindow));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if UI mode has changed due to passphrase completion.
|
|
|
|
if (UIModeController::getInstance()->listen(config, input))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (mCurrentView)
|
|
|
|
return mCurrentView->input(config, input);
|
|
|
|
|
|
|
|
return false;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::update(int deltaTime)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (mCurrentView)
|
|
|
|
mCurrentView->update(deltaTime);
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
updateSelf(deltaTime);
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void ViewController::render(const Transform4x4f& parentTrans)
|
2014-06-25 16:29:58 +00:00
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
Transform4x4f trans = mCamera * parentTrans;
|
|
|
|
Transform4x4f transInverse;
|
|
|
|
transInverse.invert(trans);
|
|
|
|
|
|
|
|
// Camera position, position + size.
|
|
|
|
Vector3f viewStart = transInverse.translation();
|
2020-09-15 20:57:54 +00:00
|
|
|
Vector3f viewEnd = transInverse * Vector3f(static_cast<float>(Renderer::getScreenWidth()),
|
|
|
|
static_cast<float>(Renderer::getScreenHeight(), 0));
|
2020-06-21 12:25:28 +00:00
|
|
|
|
|
|
|
// Keep track of UI mode changes.
|
|
|
|
UIModeController::getInstance()->monitorUIMode();
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// Draw the system view if it's flagged to be rendered.
|
|
|
|
// If the camera is moving, we're transitioning and in that case render it regardless
|
|
|
|
// of whether it's flagged for rendering or not. (Otherwise there will be a black portion
|
|
|
|
// shown on the screen during the animation).
|
|
|
|
if (getSystemListView()->getRenderView() || isCameraMoving())
|
|
|
|
getSystemListView()->render(trans);
|
2020-06-21 12:25:28 +00:00
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
// Draw the gamelists.
|
2020-06-21 12:25:28 +00:00
|
|
|
for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) {
|
2020-09-15 20:57:54 +00:00
|
|
|
// Same thing as for the system view, limit the rendering only to what needs to be drawn.
|
|
|
|
if (it->second->getRenderView() || isCameraMoving()) {
|
|
|
|
// Clipping.
|
|
|
|
Vector3f guiStart = it->second->getPosition();
|
|
|
|
Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(),
|
|
|
|
it->second->getSize().y(), 0);
|
|
|
|
|
|
|
|
if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
|
|
|
|
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
|
|
|
|
it->second->render(trans);
|
|
|
|
}
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mWindow->peekGui() == this)
|
|
|
|
mWindow->renderHelpPromptsEarly();
|
|
|
|
|
|
|
|
// Fade out.
|
|
|
|
if (mFadeOpacity) {
|
2020-09-15 20:57:54 +00:00
|
|
|
unsigned int fadeColor = 0x00000000 | static_cast<unsigned char>(mFadeOpacity * 255);
|
2020-06-21 12:25:28 +00:00
|
|
|
Renderer::setMatrix(parentTrans);
|
|
|
|
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
|
|
|
Renderer::getScreenHeight(), fadeColor, fadeColor);
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::preload()
|
|
|
|
{
|
2020-09-15 20:57:54 +00:00
|
|
|
unsigned int systemCount = SystemData::sSystemVector.size();
|
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
for (auto it = SystemData::sSystemVector.cbegin();
|
2020-09-15 20:57:54 +00:00
|
|
|
it != SystemData::sSystemVector.cend(); it ++) {
|
2020-06-21 12:25:28 +00:00
|
|
|
if (Settings::getInstance()->getBool("SplashScreen") &&
|
2020-09-15 20:57:54 +00:00
|
|
|
Settings::getInstance()->getBool("SplashScreenProgress")) {
|
|
|
|
mWindow->renderLoadingScreen("Loading '" + (*it)->getFullName() + "' (" +
|
|
|
|
std::to_string(std::distance(SystemData::sSystemVector.cbegin(), it)+1) +
|
|
|
|
"/" + std::to_string(systemCount) + ")");
|
2020-06-21 12:25:28 +00:00
|
|
|
}
|
|
|
|
(*it)->getIndex()->resetFilters();
|
|
|
|
getGameListView(*it);
|
|
|
|
}
|
|
|
|
// Load navigation sounds, but only if at least one system exists.
|
2020-09-15 20:57:54 +00:00
|
|
|
if (systemCount > 0)
|
2020-06-21 12:25:28 +00:00
|
|
|
NavigationSounds::getInstance()->loadThemeNavigationSounds(
|
|
|
|
SystemData::sSystemVector.front()->getTheme());
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) {
|
|
|
|
if (it->second.get() == view) {
|
|
|
|
bool isCurrent = (mCurrentView == it->second);
|
|
|
|
SystemData* system = it->first;
|
|
|
|
FileData* cursor = view->getCursor();
|
|
|
|
mGameListViews.erase(it);
|
|
|
|
|
|
|
|
if (reloadTheme)
|
|
|
|
system->loadTheme();
|
|
|
|
system->getIndex()->setUIModeFilters();
|
|
|
|
std::shared_ptr<IGameListView> newView = getGameListView(system);
|
|
|
|
|
|
|
|
// To counter having come from a placeholder.
|
|
|
|
if (!cursor->isPlaceHolder()) {
|
|
|
|
newView->setCursor(cursor);
|
|
|
|
}
|
|
|
|
if (isCurrent)
|
|
|
|
mCurrentView = newView;
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-07-18 11:21:44 +00:00
|
|
|
|
2020-08-23 15:04:30 +00:00
|
|
|
#if defined(_WIN64)
|
2020-07-19 20:08:14 +00:00
|
|
|
// This code is only needed for Windows, where we may need to keep ES running while
|
|
|
|
// the game/emulator is in use. It's basically used to pause any playing game video
|
|
|
|
// and to keep the screensaver from activating.
|
|
|
|
if (Settings::getInstance()->getBool("RunInBackground")) {
|
|
|
|
// If a game has been launched, then update all the GUI components to reflect this.
|
|
|
|
if (mWindow->getGameLaunchedState())
|
|
|
|
mWindow->setLaunchedGame();
|
|
|
|
}
|
2020-07-18 21:07:02 +00:00
|
|
|
#endif
|
2020-07-18 11:21:44 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
// Redisplay the current view.
|
2020-09-15 20:57:54 +00:00
|
|
|
if (mCurrentView) {
|
2020-06-21 12:25:28 +00:00
|
|
|
mCurrentView->onShow();
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->setRenderView(true);
|
|
|
|
}
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void ViewController::reloadAll()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
// Clear all GameListViews.
|
|
|
|
std::map<SystemData*, FileData*> cursorMap;
|
|
|
|
for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
|
|
|
|
cursorMap[it->first] = it->second->getCursor();
|
|
|
|
|
|
|
|
mGameListViews.clear();
|
|
|
|
|
|
|
|
// Load themes, create GameListViews and reset filters.
|
|
|
|
for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++) {
|
|
|
|
it->first->loadTheme();
|
|
|
|
it->first->getIndex()->resetFilters();
|
|
|
|
getGameListView(it->first)->setCursor(it->second);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rebuild SystemListView.
|
|
|
|
mSystemListView.reset();
|
|
|
|
getSystemListView();
|
|
|
|
|
|
|
|
// Update mCurrentView since the pointers changed.
|
|
|
|
if (mState.viewing == GAME_LIST) {
|
|
|
|
mCurrentView = getGameListView(mState.getSystem());
|
|
|
|
}
|
|
|
|
else if (mState.viewing == SYSTEM_SELECT) {
|
|
|
|
SystemData* system = mState.getSystem();
|
|
|
|
goToSystemView(SystemData::sSystemVector.front());
|
|
|
|
mSystemListView->goToSystem(system, false);
|
|
|
|
mCurrentView = mSystemListView;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
goToSystemView(SystemData::sSystemVector.front());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load navigation sounds.
|
|
|
|
NavigationSounds::getInstance()->deinit();
|
|
|
|
NavigationSounds::getInstance()->loadThemeNavigationSounds(
|
|
|
|
SystemData::sSystemVector.front()->getTheme());
|
|
|
|
|
2020-09-15 20:57:54 +00:00
|
|
|
mCurrentView->onShow();
|
|
|
|
mCurrentView->setRenderView(true);
|
2020-06-21 12:25:28 +00:00
|
|
|
updateHelpPrompts();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> ViewController::getHelpPrompts()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
std::vector<HelpPrompt> prompts;
|
|
|
|
if (!mCurrentView)
|
|
|
|
return prompts;
|
|
|
|
|
|
|
|
prompts = mCurrentView->getHelpPrompts();
|
|
|
|
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
2020-07-09 17:24:20 +00:00
|
|
|
!Settings::getInstance()->getBool("ShowKidStartMenu")))
|
2020-06-21 12:25:28 +00:00
|
|
|
prompts.push_back(HelpPrompt("start", "menu"));
|
|
|
|
return prompts;
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
HelpStyle ViewController::getHelpStyle()
|
|
|
|
{
|
2020-06-21 12:25:28 +00:00
|
|
|
if (!mCurrentView)
|
|
|
|
return GuiComponent::getHelpStyle();
|
2014-06-25 16:29:58 +00:00
|
|
|
|
2020-06-21 12:25:28 +00:00
|
|
|
return mCurrentView->getHelpStyle();
|
2014-06-25 16:29:58 +00:00
|
|
|
}
|