mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-01-17 22:55:38 +00:00
Monster commit. Rewrote gamelist sorting logic and made per-gamelist sort settings session-permanent. Cleaned up a lot of code and started to reformat for 100 characters line length.
This commit is contained in:
parent
f806285e06
commit
f2f7d34bb6
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,22 @@
|
|||
//
|
||||
// CollectionSystemManager.h
|
||||
//
|
||||
// Manages collections of the following two types:
|
||||
// 1) Automatically populated (All games, Favorites and Recent/Last Played)
|
||||
// 2) Custom/user-created (could be any number of these)
|
||||
//
|
||||
// The automatic collections are basically virtual systems that have no
|
||||
// gamelist.xml files and that only exist in memory during the program session.
|
||||
// SystemData sets up the basic data structures and CollectionSystemManager
|
||||
// populates and manages the collections.
|
||||
//
|
||||
// The custom collections have simple data files which are just lists of ROM files.
|
||||
//
|
||||
// In addition to this, CollectionSystemManager also handles some logic for
|
||||
// normal systems such as adding and removing favorite games, including triggering
|
||||
// the required re-sort and refresh of the gamelists.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_COLLECTION_SYSTEM_MANAGER_H
|
||||
#define ES_APP_COLLECTION_SYSTEM_MANAGER_H
|
||||
|
@ -11,26 +30,22 @@ class SystemData;
|
|||
class Window;
|
||||
struct SystemEnvironmentData;
|
||||
|
||||
enum CollectionSystemType
|
||||
{
|
||||
enum CollectionSystemType {
|
||||
AUTO_ALL_GAMES,
|
||||
AUTO_LAST_PLAYED,
|
||||
AUTO_FAVORITES,
|
||||
CUSTOM_COLLECTION
|
||||
};
|
||||
|
||||
struct CollectionSystemDecl
|
||||
{
|
||||
CollectionSystemType type; // type of system
|
||||
struct CollectionSystemDecl {
|
||||
CollectionSystemType type;
|
||||
std::string name;
|
||||
std::string longName;
|
||||
std::string defaultSort;
|
||||
std::string themeFolder;
|
||||
bool isCustom;
|
||||
};
|
||||
|
||||
struct CollectionSystemData
|
||||
{
|
||||
struct CollectionSystemData {
|
||||
SystemData* system;
|
||||
CollectionSystemDecl decl;
|
||||
bool isEnabled;
|
||||
|
@ -57,8 +72,10 @@ public:
|
|||
void updateCollectionSystem(FileData* file, CollectionSystemData sysData);
|
||||
void deleteCollectionFiles(FileData* file);
|
||||
|
||||
inline std::map<std::string, CollectionSystemData> getAutoCollectionSystems() { return mAutoCollectionSystemsData; };
|
||||
inline std::map<std::string, CollectionSystemData> getCustomCollectionSystems() { return mCustomCollectionSystemsData; };
|
||||
inline std::map<std::string, CollectionSystemData> getAutoCollectionSystems()
|
||||
{ return mAutoCollectionSystemsData; };
|
||||
inline std::map<std::string, CollectionSystemData> getCustomCollectionSystems()
|
||||
{ return mCustomCollectionSystemsData; };
|
||||
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; };
|
||||
std::vector<std::string> getUnusedSystemsFromTheme();
|
||||
SystemData* addNewCustomCollection(std::string name);
|
||||
|
@ -76,6 +93,8 @@ public:
|
|||
SystemData* getSystemToView(SystemData* sys);
|
||||
void updateCollectionFolderMetadata(SystemData* sys);
|
||||
|
||||
bool getIsCustomCollection(SystemData* system);
|
||||
|
||||
private:
|
||||
static CollectionSystemManager* sInstance;
|
||||
SystemEnvironmentData* mCollectionEnvData;
|
||||
|
@ -90,12 +109,14 @@ private:
|
|||
void initAutoCollectionSystems();
|
||||
void initCustomCollectionSystems();
|
||||
SystemData* getAllGamesCollection();
|
||||
SystemData* createNewCollectionEntry(std::string name, CollectionSystemDecl sysDecl, bool index = true);
|
||||
SystemData* createNewCollectionEntry(std::string name,
|
||||
CollectionSystemDecl sysDecl, bool index = true);
|
||||
void populateAutoCollection(CollectionSystemData* sysData);
|
||||
void populateCustomCollection(CollectionSystemData* sysData);
|
||||
|
||||
void removeCollectionsFromDisplayedSystems();
|
||||
void addEnabledCollectionsToDisplayedSystems(std::map<std::string, CollectionSystemData>* colSystemData);
|
||||
void addEnabledCollectionsToDisplayedSystems(std::map<std::string,
|
||||
CollectionSystemData>* colSystemData);
|
||||
|
||||
std::vector<std::string> getSystemsFromConfig();
|
||||
std::vector<std::string> getSystemsFromTheme();
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// FileData.cpp
|
||||
//
|
||||
// Provides game file data structures and functions to access and sort this information.
|
||||
// Also provides functions to look up paths to media files and for launching games
|
||||
// (launching initiated by the ViewController).
|
||||
//
|
||||
|
||||
#include "FileData.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
@ -16,11 +24,22 @@
|
|||
#include "Window.h"
|
||||
#include <assert.h>
|
||||
|
||||
FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system)
|
||||
: mType(type), mPath(path), mSystem(system), mEnvData(envData), mSourceFileData(NULL), mParent(NULL), metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA) // metadata is REALLY set in the constructor!
|
||||
FileData::FileData(
|
||||
FileType type,
|
||||
const std::string& path,
|
||||
SystemEnvironmentData* envData,
|
||||
SystemData* system)
|
||||
: mType(type),
|
||||
mPath(path),
|
||||
mSystem(system),
|
||||
mEnvData(envData),
|
||||
mSourceFileData(nullptr),
|
||||
mParent(nullptr),
|
||||
// Metadata is REALLY set in the constructor!
|
||||
metadata(type == GAME ? GAME_METADATA : FOLDER_METADATA)
|
||||
{
|
||||
// metadata needs at least a name field (since that's what getName() will return)
|
||||
if(metadata.get("name").empty())
|
||||
// Metadata needs at least a name field (since that's what getName() will return).
|
||||
if (metadata.get("name").empty())
|
||||
metadata.set("name", getDisplayName());
|
||||
mSystemName = system->getName();
|
||||
metadata.resetChangedFlag();
|
||||
|
@ -28,10 +47,10 @@ FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData
|
|||
|
||||
FileData::~FileData()
|
||||
{
|
||||
if(mParent)
|
||||
if (mParent)
|
||||
mParent->removeChild(this);
|
||||
|
||||
if(mType == GAME)
|
||||
if (mType == GAME)
|
||||
mSystem->getIndex()->removeFromIndex(this);
|
||||
|
||||
mChildren.clear();
|
||||
|
@ -40,9 +59,6 @@ FileData::~FileData()
|
|||
std::string FileData::getDisplayName() const
|
||||
{
|
||||
std::string stem = Utils::FileSystem::getStem(mPath);
|
||||
// if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))
|
||||
// stem = MameNames::getInstance()->getRealName(stem);
|
||||
|
||||
return stem;
|
||||
}
|
||||
|
||||
|
@ -77,25 +93,19 @@ const std::string FileData::getMediaDirectory() const
|
|||
std::string mediaDirSetting = Settings::getInstance()->getString("MediaDirectory");
|
||||
std::string mediaDirPath = "";
|
||||
|
||||
if(mediaDirSetting == "")
|
||||
{
|
||||
if (mediaDirSetting == "") {
|
||||
mediaDirPath = Utils::FileSystem::getHomePath() + "/.emulationstation/downloaded_media/";
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
mediaDirPath = mediaDirSetting;
|
||||
|
||||
// Expand home symbol if the path starts with ~
|
||||
if(mediaDirPath[0] == '~')
|
||||
{
|
||||
if (mediaDirPath[0] == '~') {
|
||||
mediaDirPath.erase(0, 1);
|
||||
mediaDirPath.insert(0, Utils::FileSystem::getHomePath());
|
||||
}
|
||||
|
||||
if(mediaDirPath.back() != '/')
|
||||
{
|
||||
if (mediaDirPath.back() != '/')
|
||||
mediaDirPath = mediaDirPath + "/";
|
||||
}
|
||||
}
|
||||
|
||||
return mediaDirPath;
|
||||
|
@ -106,21 +116,21 @@ const std::string FileData::getThumbnailPath() const
|
|||
const char* extList[2] = { ".png", ".jpg" };
|
||||
std::string tempPath = getMediaDirectory() + mSystemName + "/thumbnails/" + getDisplayName();
|
||||
|
||||
// Look for media in the media directory
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
// Look for media in the media directory.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string mediaPath = tempPath + extList[i];
|
||||
if(Utils::FileSystem::exists(mediaPath))
|
||||
if (Utils::FileSystem::exists(mediaPath))
|
||||
return mediaPath;
|
||||
}
|
||||
|
||||
// No media found in the media directory, so look for local art as well (if configured to do so)
|
||||
if(Settings::getInstance()->getBool("LocalArt"))
|
||||
// No media found in the media directory, so look
|
||||
// for local art as well (if configured to do so).
|
||||
if (Settings::getInstance()->getBool("LocalArt"))
|
||||
{
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-thumbnail" + extList[i];
|
||||
if(Utils::FileSystem::exists(localMediaPath))
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" +
|
||||
getDisplayName() + "-thumbnail" + extList[i];
|
||||
if (Utils::FileSystem::exists(localMediaPath))
|
||||
return localMediaPath;
|
||||
}
|
||||
}
|
||||
|
@ -133,21 +143,21 @@ const std::string FileData::getVideoPath() const
|
|||
const char* extList[5] = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" };
|
||||
std::string tempPath = getMediaDirectory() + mSystemName + "/videos/" + getDisplayName();
|
||||
|
||||
// Look for media in the media directory
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
// Look for media in the media directory.
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string mediaPath = tempPath + extList[i];
|
||||
if(Utils::FileSystem::exists(mediaPath))
|
||||
if (Utils::FileSystem::exists(mediaPath))
|
||||
return mediaPath;
|
||||
}
|
||||
|
||||
// No media found in the media directory, so look for local art as well (if configured to do so)
|
||||
if(Settings::getInstance()->getBool("LocalArt"))
|
||||
// No media found in the media directory, so look
|
||||
// for local art as well (if configured to do so).
|
||||
if (Settings::getInstance()->getBool("LocalArt"))
|
||||
{
|
||||
for(int i = 0; i < 5; i++)
|
||||
{
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-video" + extList[i];
|
||||
if(Utils::FileSystem::exists(localMediaPath))
|
||||
for (int i = 0; i < 5; i++) {
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() +
|
||||
"-video" + extList[i];
|
||||
if (Utils::FileSystem::exists(localMediaPath))
|
||||
return localMediaPath;
|
||||
}
|
||||
}
|
||||
|
@ -160,21 +170,21 @@ const std::string FileData::getMarqueePath() const
|
|||
const char* extList[2] = { ".png", ".jpg" };
|
||||
std::string tempPath = getMediaDirectory() + mSystemName + "/marquees/" + getDisplayName();
|
||||
|
||||
// Look for media in the media directory
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
// Look for media in the media directory.
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string mediaPath = tempPath + extList[i];
|
||||
if(Utils::FileSystem::exists(mediaPath))
|
||||
if (Utils::FileSystem::exists(mediaPath))
|
||||
return mediaPath;
|
||||
}
|
||||
|
||||
// No media found in the media directory, so look for local art as well (if configured to do so)
|
||||
if(Settings::getInstance()->getBool("LocalArt"))
|
||||
// No media found in the media directory, so look
|
||||
// for local art as well (if configured to do so).
|
||||
if (Settings::getInstance()->getBool("LocalArt"))
|
||||
{
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-marquee" + extList[i];
|
||||
if(Utils::FileSystem::exists(localMediaPath))
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() +
|
||||
"-marquee" + extList[i];
|
||||
if (Utils::FileSystem::exists(localMediaPath))
|
||||
return localMediaPath;
|
||||
}
|
||||
}
|
||||
|
@ -186,32 +196,30 @@ const std::string FileData::getImagePath() const
|
|||
{
|
||||
const char* extList[2] = { ".png", ".jpg" };
|
||||
|
||||
// Look for mix image (a combination of screenshot, 3D box and marquee) in the media directory
|
||||
// Look for mix image (a combination of screenshot, 3D box and marquee) in the media directory.
|
||||
std::string tempPath = getMediaDirectory() + mSystemName + "/miximages/" + getDisplayName();
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string mediaPath = tempPath + extList[i];
|
||||
if(Utils::FileSystem::exists(mediaPath))
|
||||
if (Utils::FileSystem::exists(mediaPath))
|
||||
return mediaPath;
|
||||
}
|
||||
|
||||
// If no mix image exists, try normal screenshot
|
||||
// If no mix image exists, try normal screenshot.
|
||||
tempPath = getMediaDirectory() + mSystemName + "/screenshots/" + getDisplayName();
|
||||
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string mediaPath = tempPath + extList[i];
|
||||
if(Utils::FileSystem::exists(mediaPath))
|
||||
if (Utils::FileSystem::exists(mediaPath))
|
||||
return mediaPath;
|
||||
}
|
||||
|
||||
// No media found in the media directory, so look for local art as well (if configured to do so)
|
||||
if(Settings::getInstance()->getBool("LocalArt"))
|
||||
{
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-image" + extList[i];
|
||||
if(Utils::FileSystem::exists(localMediaPath))
|
||||
// No media found in the media directory, so look
|
||||
// for local art as well (if configured to do so).
|
||||
if (Settings::getInstance()->getBool("LocalArt")) {
|
||||
for (int i = 0; i < 2; i++) {
|
||||
std::string localMediaPath = mEnvData->mStartPath + "/images/" +
|
||||
getDisplayName() + "-image" + extList[i];
|
||||
if (Utils::FileSystem::exists(localMediaPath))
|
||||
return localMediaPath;
|
||||
}
|
||||
}
|
||||
|
@ -225,17 +233,14 @@ const std::vector<FileData*>& FileData::getChildrenListToDisplay()
|
|||
FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex();
|
||||
if (idx->isFiltered()) {
|
||||
mFilteredChildren.clear();
|
||||
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
{
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||
if (idx->showFile((*it))) {
|
||||
mFilteredChildren.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
return mFilteredChildren;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
return mChildren;
|
||||
}
|
||||
}
|
||||
|
@ -245,16 +250,12 @@ std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, bool d
|
|||
std::vector<FileData*> out;
|
||||
FileFilterIndex* idx = mSystem->getIndex();
|
||||
|
||||
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
{
|
||||
if((*it)->getType() & typeMask)
|
||||
{
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||
if ((*it)->getType() & typeMask) {
|
||||
if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it))
|
||||
out.push_back(*it);
|
||||
}
|
||||
|
||||
if((*it)->getChildren().size() > 0)
|
||||
{
|
||||
if ((*it)->getChildren().size() > 0) {
|
||||
std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly);
|
||||
out.insert(out.cend(), subchildren.cbegin(), subchildren.cend());
|
||||
}
|
||||
|
@ -271,9 +272,10 @@ const bool FileData::isArcadeAsset()
|
|||
{
|
||||
const std::string stem = Utils::FileSystem::getStem(mPath);
|
||||
return (
|
||||
(mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO)))
|
||||
&&
|
||||
(MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem))
|
||||
(mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
|
||||
mSystem->hasPlatformId(PlatformIds::NEOGEO))) &&
|
||||
(MameNames::getInstance()->isBios(stem) ||
|
||||
MameNames::getInstance()->isDevice(stem))
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -288,8 +290,7 @@ void FileData::addChild(FileData* file)
|
|||
assert(file->getParent() == NULL);
|
||||
|
||||
const std::string key = file->getKey();
|
||||
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend())
|
||||
{
|
||||
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
|
||||
mChildrenByFilename[key] = file;
|
||||
mChildren.push_back(file);
|
||||
file->mParent = this;
|
||||
|
@ -301,10 +302,8 @@ void FileData::removeChild(FileData* file)
|
|||
assert(mType == FOLDER);
|
||||
assert(file->getParent() == this);
|
||||
mChildrenByFilename.erase(file->getKey());
|
||||
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
{
|
||||
if(*it == file)
|
||||
{
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||
if (*it == file) {
|
||||
file->mParent = NULL;
|
||||
mChildren.erase(it);
|
||||
return;
|
||||
|
@ -320,19 +319,59 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending)
|
|||
{
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
|
||||
|
||||
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
{
|
||||
if((*it)->getChildren().size() > 0)
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sort(comparator, ascending);
|
||||
}
|
||||
|
||||
if(!ascending)
|
||||
if (!ascending)
|
||||
std::reverse(mChildren.begin(), mChildren.end());
|
||||
}
|
||||
|
||||
void FileData::sort(const SortType& type)
|
||||
void FileData::sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending)
|
||||
{
|
||||
sort(*type.comparisonFunction, type.ascending);
|
||||
std::vector<FileData*> mChildrenFavorites;
|
||||
std::vector<FileData*> mChildrenOthers;
|
||||
|
||||
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||
if (mChildren[i]->getFavorite())
|
||||
mChildrenFavorites.push_back(mChildren[i]);
|
||||
else
|
||||
mChildrenOthers.push_back(mChildren[i]);
|
||||
}
|
||||
|
||||
// Sort favorite games and the other games separately.
|
||||
std::stable_sort(mChildrenFavorites.begin(), mChildrenFavorites.end(), comparator);
|
||||
std::stable_sort(mChildrenOthers.begin(), mChildrenOthers.end(), comparator);
|
||||
|
||||
for (auto it = mChildrenFavorites.cbegin(); it != mChildrenFavorites.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sortFavoritesOnTop(comparator, ascending);
|
||||
}
|
||||
|
||||
for (auto it = mChildrenOthers.cbegin(); it != mChildrenOthers.cend(); it++) {
|
||||
if ((*it)->getChildren().size() > 0)
|
||||
(*it)->sortFavoritesOnTop(comparator, ascending);
|
||||
}
|
||||
|
||||
if (!ascending) {
|
||||
std::reverse(mChildrenFavorites.begin(), mChildrenFavorites.end());
|
||||
std::reverse(mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
|
||||
// Combine the individually sorted favorite games and other games vectors.
|
||||
mChildren.erase(mChildren.begin(), mChildren.end());
|
||||
mChildren.reserve(mChildrenFavorites.size() + mChildrenOthers.size());
|
||||
mChildren.insert(mChildren.end(), mChildrenFavorites.begin(), mChildrenFavorites.end());
|
||||
mChildren.insert(mChildren.end(), mChildrenOthers.begin(), mChildrenOthers.end());
|
||||
}
|
||||
|
||||
void FileData::sort(const SortType& type, bool mFavoritesOnTop)
|
||||
{
|
||||
if (mFavoritesOnTop)
|
||||
sortFavoritesOnTop(*type.comparisonFunction, type.ascending);
|
||||
else
|
||||
sort(*type.comparisonFunction, type.ascending);
|
||||
}
|
||||
|
||||
void FileData::launchGame(Window* window)
|
||||
|
@ -346,15 +385,13 @@ void FileData::launchGame(Window* window)
|
|||
|
||||
std::string command = "";
|
||||
|
||||
// Check if there is a launch string override for the game and the corresponding option has been set
|
||||
if(Settings::getInstance()->getBool("LaunchstringOverride") && !metadata.get("launchstring").empty())
|
||||
{
|
||||
// Check if there is a launch string override for the game
|
||||
// and the corresponding option to use it has been set.
|
||||
if (Settings::getInstance()->getBool("LaunchstringOverride") &&
|
||||
!metadata.get("launchstring").empty())
|
||||
command = metadata.get("launchstring");
|
||||
}
|
||||
else
|
||||
{
|
||||
command = mEnvData->mLaunchCommand;
|
||||
}
|
||||
|
||||
const std::string rom = Utils::FileSystem::getEscapedPath(getPath());
|
||||
const std::string basename = Utils::FileSystem::getStem(getPath());
|
||||
|
@ -369,25 +406,23 @@ void FileData::launchGame(Window* window)
|
|||
LOG(LogInfo) << " " << command;
|
||||
int exitCode = runSystemCommand(command);
|
||||
|
||||
if(exitCode != 0)
|
||||
{
|
||||
if (exitCode != 0)
|
||||
LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
|
||||
}
|
||||
|
||||
Scripting::fireEvent("game-end");
|
||||
|
||||
// window->init();
|
||||
|
||||
VolumeControl::getInstance()->init();
|
||||
window->normalizeNextUpdate();
|
||||
|
||||
//update number of times the game has been launched
|
||||
|
||||
// Update number of times the game has been launched.
|
||||
FileData* gameToUpdate = getSourceFileData();
|
||||
|
||||
int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1;
|
||||
gameToUpdate->metadata.set("playcount", std::to_string(static_cast<long long>(timesPlayed)));
|
||||
|
||||
//update last played time
|
||||
// Update last played time.
|
||||
gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now()));
|
||||
CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate);
|
||||
|
||||
|
@ -395,9 +430,10 @@ void FileData::launchGame(Window* window)
|
|||
}
|
||||
|
||||
CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
|
||||
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(), file->getSourceFileData()->getSystemEnvData(), system)
|
||||
: FileData(file->getSourceFileData()->getType(), file->getSourceFileData()->getPath(),
|
||||
file->getSourceFileData()->getSystemEnvData(), system)
|
||||
{
|
||||
// we use this constructor to create a clone of the filedata, and change its system
|
||||
// We use this constructor to create a clone of the filedata, and change its system.
|
||||
mSourceFileData = file->getSourceFileData();
|
||||
refreshMetadata();
|
||||
mParent = NULL;
|
||||
|
@ -407,8 +443,8 @@ CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
|
|||
|
||||
CollectionFileData::~CollectionFileData()
|
||||
{
|
||||
// need to remove collection file data at the collection object destructor
|
||||
if(mParent)
|
||||
// Need to remove collection file data at the collection object destructor.
|
||||
if (mParent)
|
||||
mParent->removeChild(this);
|
||||
mParent = NULL;
|
||||
}
|
||||
|
@ -431,28 +467,28 @@ void CollectionFileData::refreshMetadata()
|
|||
const std::string& CollectionFileData::getName()
|
||||
{
|
||||
if (mDirty) {
|
||||
mCollectionFileName = Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
|
||||
mCollectionFileName += " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
|
||||
mCollectionFileName =
|
||||
Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
|
||||
mCollectionFileName +=
|
||||
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
|
||||
mDirty = false;
|
||||
}
|
||||
|
||||
if (Settings::getInstance()->getBool("CollectionShowSystemInfo"))
|
||||
return mCollectionFileName;
|
||||
|
||||
return mSourceFileData->metadata.get("name");
|
||||
}
|
||||
|
||||
// returns Sort Type based on a string description
|
||||
// Return sort type based on a string description.
|
||||
FileData::SortType getSortTypeFromString(std::string desc) {
|
||||
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
|
||||
// find it
|
||||
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
|
||||
{
|
||||
// Find it
|
||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
if(sort.description == desc)
|
||||
{
|
||||
if (sort.description == desc)
|
||||
return sort;
|
||||
}
|
||||
}
|
||||
// if not found default to name, ascending
|
||||
// If no type found then default to "filename, ascending".
|
||||
return FileSorts::SortTypes.at(0);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// FileData.h
|
||||
//
|
||||
// Provides game file data structures and functions to access and sort this information.
|
||||
// Also provides functions to look up paths to media files and for launching games
|
||||
// (launching initiated by the ViewController).
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_FILE_DATA_H
|
||||
#define ES_APP_FILE_DATA_H
|
||||
|
@ -10,15 +18,13 @@ class SystemData;
|
|||
class Window;
|
||||
struct SystemEnvironmentData;
|
||||
|
||||
enum FileType
|
||||
{
|
||||
enum FileType {
|
||||
GAME = 1, // Cannot have children.
|
||||
FOLDER = 2,
|
||||
PLACEHOLDER = 3
|
||||
};
|
||||
|
||||
enum FileChangeType
|
||||
{
|
||||
enum FileChangeType {
|
||||
FILE_ADDED,
|
||||
FILE_METADATA_CHANGED,
|
||||
FILE_REMOVED,
|
||||
|
@ -33,7 +39,11 @@ FileType stringToFileType(const char* str);
|
|||
class FileData
|
||||
{
|
||||
public:
|
||||
FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system);
|
||||
FileData(FileType type,
|
||||
const std::string& path,
|
||||
SystemEnvironmentData* envData,
|
||||
SystemData* system);
|
||||
|
||||
virtual ~FileData();
|
||||
|
||||
virtual const std::string& getName();
|
||||
|
@ -42,7 +52,8 @@ public:
|
|||
inline FileType getType() const { return mType; }
|
||||
inline const std::string& getPath() const { return mPath; }
|
||||
inline FileData* getParent() const { return mParent; }
|
||||
inline const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const { return mChildrenByFilename; }
|
||||
inline const std::unordered_map<std::string, FileData*>& getChildrenByFilename() const
|
||||
{ return mChildrenByFilename; }
|
||||
inline const std::vector<FileData*>& getChildren() const { return mChildren; }
|
||||
inline SystemData* getSystem() const { return mSystem; }
|
||||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
|
@ -53,7 +64,8 @@ public:
|
|||
virtual const std::string getImagePath() const;
|
||||
|
||||
const std::vector<FileData*>& getChildrenListToDisplay();
|
||||
std::vector<FileData*> getFilesRecursive(unsigned int typeMask, bool displayedOnly = false) const;
|
||||
std::vector<FileData*> getFilesRecursive(unsigned int typeMask,
|
||||
bool displayedOnly = false) const;
|
||||
|
||||
void addChild(FileData* file); // Error if mType != FOLDER
|
||||
void removeChild(FileData* file); //Error if mType != FOLDER
|
||||
|
@ -69,33 +81,42 @@ public:
|
|||
virtual FileData* getSourceFileData();
|
||||
inline std::string getSystemName() const { return mSystemName; };
|
||||
|
||||
// Returns our best guess at the "real" name for this file (will attempt to perform MAME name translation)
|
||||
// Returns our best guess at the "real" name for this file
|
||||
// (will attempt to perform MAME name translation).
|
||||
std::string getDisplayName() const;
|
||||
|
||||
// As above, but also remove parenthesis
|
||||
// As above, but also remove parenthesis.
|
||||
std::string getCleanName() const;
|
||||
|
||||
void launchGame(Window* window);
|
||||
|
||||
typedef bool ComparisonFunction(const FileData* a, const FileData* b);
|
||||
struct SortType
|
||||
{
|
||||
struct SortType {
|
||||
ComparisonFunction* comparisonFunction;
|
||||
bool ascending;
|
||||
std::string description;
|
||||
|
||||
SortType(ComparisonFunction* sortFunction, bool sortAscending, const std::string & sortDescription)
|
||||
: comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {}
|
||||
SortType(ComparisonFunction* sortFunction,
|
||||
bool sortAscending,
|
||||
const std::string& sortDescription)
|
||||
: comparisonFunction(sortFunction),
|
||||
ascending(sortAscending),
|
||||
description(sortDescription) {}
|
||||
};
|
||||
|
||||
void sort(ComparisonFunction& comparator, bool ascending = true);
|
||||
void sort(const SortType& type);
|
||||
void sortFavoritesOnTop(ComparisonFunction& comparator, bool ascending = true);
|
||||
void sort(const SortType& type, bool mFavoritesOnTop = false);
|
||||
MetaDataList metadata;
|
||||
|
||||
inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
|
||||
inline std::string getSortTypeString() { return mSortTypeString; }
|
||||
|
||||
protected:
|
||||
FileData* mSourceFileData;
|
||||
FileData* mParent;
|
||||
std::string mSystemName;
|
||||
std::string mSortTypeString = "";
|
||||
|
||||
private:
|
||||
FileType mType;
|
||||
|
@ -117,7 +138,7 @@ public:
|
|||
FileData* getSourceFileData();
|
||||
std::string getKey();
|
||||
private:
|
||||
// needs to be updated when metadata changes
|
||||
// Needs to be updated when metadata changes.
|
||||
std::string mCollectionFileName;
|
||||
bool mDirty;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// Gamelist.cpp
|
||||
//
|
||||
// Parses and updates the gamelist.xml files.
|
||||
//
|
||||
|
||||
#include "Gamelist.h"
|
||||
|
||||
#include <chrono>
|
||||
|
@ -12,24 +18,24 @@
|
|||
|
||||
FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType type)
|
||||
{
|
||||
// first, verify that path is within the system's root folder
|
||||
// First, verify that path is within the system's root folder.
|
||||
FileData* root = system->getRootFolder();
|
||||
bool contains = false;
|
||||
std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains);
|
||||
|
||||
if(!contains)
|
||||
{
|
||||
LOG(LogError) << "File path \"" << path << "\" is outside system path \"" << system->getStartPath() << "\"";
|
||||
return NULL;
|
||||
if (!contains) {
|
||||
LOG(LogError) << "File path \"" << path << "\" is outside system path \"" <<
|
||||
system->getStartPath() << "\"";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Utils::FileSystem::stringList pathList = Utils::FileSystem::getPathList(relative);
|
||||
auto path_it = pathList.begin();
|
||||
FileData* treeNode = root;
|
||||
bool found = false;
|
||||
while(path_it != pathList.end())
|
||||
{
|
||||
const std::unordered_map<std::string, FileData*>& children = treeNode->getChildrenByFilename();
|
||||
while (path_it != pathList.end()) {
|
||||
const std::unordered_map<std::string, FileData*>& children =
|
||||
treeNode->getChildrenByFilename();
|
||||
|
||||
std::string key = *path_it;
|
||||
found = children.find(key) != children.cend();
|
||||
|
@ -37,40 +43,35 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
|
|||
treeNode = children.at(key);
|
||||
}
|
||||
|
||||
// this is the end
|
||||
if(path_it == --pathList.end())
|
||||
{
|
||||
if(found)
|
||||
// This is the end
|
||||
if (path_it == --pathList.end()) {
|
||||
if (found)
|
||||
return treeNode;
|
||||
|
||||
if(type == FOLDER)
|
||||
{
|
||||
LOG(LogWarning) << "gameList: folder doesn't already exist, won't create";
|
||||
return NULL;
|
||||
if (type == FOLDER) {
|
||||
LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FileData* file = new FileData(type, path, system->getSystemEnvData(), system);
|
||||
|
||||
// skipping arcade assets from gamelist
|
||||
if(!file->isArcadeAsset())
|
||||
{
|
||||
// Skipping arcade assets from gamelist.
|
||||
if (!file->isArcadeAsset())
|
||||
treeNode->addChild(file);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
if(!found)
|
||||
{
|
||||
// don't create folders unless it's leading up to a game
|
||||
// if type is a folder it's gonna be empty, so don't bother
|
||||
if(type == FOLDER)
|
||||
{
|
||||
LOG(LogWarning) << "gameList: folder doesn't already exist, won't create";
|
||||
return NULL;
|
||||
if (!found) {
|
||||
// Don't create folders unless they're including any games.
|
||||
// If the type is FOLDER it's going to be empty, so don't bother.
|
||||
if (type == FOLDER) {
|
||||
LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create";
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// create missing folder
|
||||
FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it, system->getSystemEnvData(), system);
|
||||
// Create missing folder.
|
||||
FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath())
|
||||
+ "/" + *path_it, system->getSystemEnvData(), system);
|
||||
treeNode->addChild(folder);
|
||||
treeNode = folder;
|
||||
}
|
||||
|
@ -78,7 +79,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
|
|||
path_it++;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void parseGamelist(SystemData* system)
|
||||
|
@ -86,7 +87,7 @@ void parseGamelist(SystemData* system)
|
|||
bool trustGamelist = Settings::getInstance()->getBool("ParseGamelistOnly");
|
||||
std::string xmlpath = system->getGamelistPath(false);
|
||||
|
||||
if(!Utils::FileSystem::exists(xmlpath))
|
||||
if (!Utils::FileSystem::exists(xmlpath))
|
||||
return;
|
||||
|
||||
LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\"...";
|
||||
|
@ -94,15 +95,14 @@ void parseGamelist(SystemData* system)
|
|||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||
|
||||
if(!result)
|
||||
{
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description();
|
||||
if (!result) {
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlpath <<
|
||||
"\"!\n " <<result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
pugi::xml_node root = doc.child("gameList");
|
||||
if(!root)
|
||||
{
|
||||
if (!root) {
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!";
|
||||
return;
|
||||
}
|
||||
|
@ -111,33 +111,32 @@ void parseGamelist(SystemData* system)
|
|||
|
||||
const char* tagList[2] = { "game", "folder" };
|
||||
FileType typeList[2] = { GAME, FOLDER };
|
||||
for(int i = 0; i < 2; i++)
|
||||
{
|
||||
for (int i = 0; i < 2; i++) {
|
||||
const char* tag = tagList[i];
|
||||
FileType type = typeList[i];
|
||||
for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag))
|
||||
{
|
||||
const std::string path = Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(), relativeTo, false);
|
||||
for (pugi::xml_node fileNode = root.child(tag); fileNode; fileNode =
|
||||
fileNode.next_sibling(tag)) {
|
||||
const std::string path =
|
||||
Utils::FileSystem::resolveRelativePath(fileNode.child("path").text().get(),
|
||||
relativeTo, false);
|
||||
|
||||
if(!trustGamelist && !Utils::FileSystem::exists(path))
|
||||
{
|
||||
if (!trustGamelist && !Utils::FileSystem::exists(path)) {
|
||||
LOG(LogWarning) << "File \"" << path << "\" does not exist! Ignoring.";
|
||||
continue;
|
||||
}
|
||||
|
||||
FileData* file = findOrCreateFile(system, path, type);
|
||||
if(!file)
|
||||
{
|
||||
LOG(LogError) << "Error finding/creating FileData for \"" << path << "\", skipping.";
|
||||
if (!file) {
|
||||
LOG(LogError) << "Error finding/creating FileData for \"" <<
|
||||
path << "\", skipping.";
|
||||
continue;
|
||||
}
|
||||
else if(!file->isArcadeAsset())
|
||||
{
|
||||
else if (!file->isArcadeAsset()) {
|
||||
std::string defaultName = file->metadata.get("name");
|
||||
file->metadata = MetaDataList::createFromXML(GAME_METADATA, fileNode, relativeTo);
|
||||
|
||||
//make sure name gets set if one didn't exist
|
||||
if(file->metadata.get("name").empty())
|
||||
// Make sure a name gets set if one doesn't exist.
|
||||
if (file->metadata.get("name").empty())
|
||||
file->metadata.set("name", defaultName);
|
||||
|
||||
file->metadata.resetChangedFlag();
|
||||
|
@ -146,128 +145,137 @@ void parseGamelist(SystemData* system)
|
|||
}
|
||||
}
|
||||
|
||||
void addFileDataNode(pugi::xml_node& parent, const FileData* file, const char* tag, SystemData* system)
|
||||
void addFileDataNode(pugi::xml_node& parent, const FileData* file,
|
||||
const char* tag, SystemData* system)
|
||||
{
|
||||
//create game and add to parent node
|
||||
// Create game and add to parent node.
|
||||
pugi::xml_node newNode = parent.append_child(tag);
|
||||
|
||||
//write metadata
|
||||
// Write metadata.
|
||||
file->metadata.appendToXML(newNode, true, system->getStartPath());
|
||||
|
||||
if(newNode.children().begin() == newNode.child("name") //first element is name
|
||||
&& ++newNode.children().begin() == newNode.children().end() //theres only one element
|
||||
&& newNode.child("name").text().get() == file->getDisplayName()) //the name is the default
|
||||
{
|
||||
//if the only info is the default name, don't bother with this node
|
||||
//delete it and ultimately do nothing
|
||||
parent.remove_child(newNode);
|
||||
}else{
|
||||
//there's something useful in there so we'll keep the node, add the path
|
||||
// First element is "name", there's only one element and the name is the default.
|
||||
if (newNode.children().begin() == newNode.child("name") &&
|
||||
++newNode.children().begin() == newNode.children().end() &&
|
||||
newNode.child("name").text().get() == file->getDisplayName()) {
|
||||
|
||||
// try and make the path relative if we can so things still work if we change the rom folder location in the future
|
||||
newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false).c_str());
|
||||
// If the only info is the default name, don't bother
|
||||
// with this node, delete it and ultimately do nothing.
|
||||
parent.remove_child(newNode);
|
||||
}
|
||||
else {
|
||||
// There's something useful in there so we'll keep the node, add the path.
|
||||
|
||||
// Try and make the path relative if we can so things still
|
||||
// work if we change the ROM folder location in the future.
|
||||
newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file->
|
||||
getPath(), system->getStartPath(), false).c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void updateGamelist(SystemData* system)
|
||||
{
|
||||
//We do this by reading the XML again, adding changes and then writing it back,
|
||||
//because there might be information missing in our systemdata which would then miss in the new XML.
|
||||
//We have the complete information for every game though, so we can simply remove a game
|
||||
//we already have in the system from the XML, and then add it back from its GameData information...
|
||||
|
||||
if(Settings::getInstance()->getBool("IgnoreGamelist"))
|
||||
// We do this by reading the XML again, adding changes and then writing them back,
|
||||
// because there might be information missing in our systemdata which we would otherwise
|
||||
// miss in the new XML file. We have the complete information for every game though, so
|
||||
// we can simply remove a game we already have in the system from the XML, and then add
|
||||
// it back from its GameData information...
|
||||
if (Settings::getInstance()->getBool("IgnoreGamelist"))
|
||||
return;
|
||||
|
||||
pugi::xml_document doc;
|
||||
pugi::xml_node root;
|
||||
std::string xmlReadPath = system->getGamelistPath(false);
|
||||
|
||||
if(Utils::FileSystem::exists(xmlReadPath))
|
||||
{
|
||||
//parse an existing file first
|
||||
if (Utils::FileSystem::exists(xmlReadPath)) {
|
||||
// Parse an existing file first.
|
||||
pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str());
|
||||
|
||||
if(!result)
|
||||
{
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " << result.description();
|
||||
if (!result) {
|
||||
LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " <<
|
||||
result.description();
|
||||
return;
|
||||
}
|
||||
|
||||
root = doc.child("gameList");
|
||||
if(!root)
|
||||
{
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlReadPath << "\"!";
|
||||
if (!root) {
|
||||
LOG(LogError) << "Could not find <gameList> node in gamelist \"" <<
|
||||
xmlReadPath << "\"!";
|
||||
return;
|
||||
}
|
||||
}else{
|
||||
//set up an empty gamelist to append to
|
||||
}
|
||||
else {
|
||||
// Set up an empty gamelist to append to.
|
||||
root = doc.append_child("gameList");
|
||||
}
|
||||
|
||||
|
||||
//now we have all the information from the XML. now iterate through all our games and add information from there
|
||||
// Now we have all the information from the XML file, so iterate
|
||||
// through all our games and add the information from there.
|
||||
FileData* rootFolder = system->getRootFolder();
|
||||
if (rootFolder != nullptr)
|
||||
{
|
||||
if (rootFolder != nullptr) {
|
||||
int numUpdated = 0;
|
||||
|
||||
//get only files, no folders
|
||||
// Get only files, no folders.
|
||||
std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER);
|
||||
//iterate through all files, checking if they're already in the XML
|
||||
for(std::vector<FileData*>::const_iterator fit = files.cbegin(); fit != files.cend(); ++fit)
|
||||
{
|
||||
// Iterate through all files, checking if they're already in the XML file.
|
||||
for (std::vector<FileData*>::const_iterator fit = files.cbegin();
|
||||
fit != files.cend(); ++fit) {
|
||||
const char* tag = ((*fit)->getType() == GAME) ? "game" : "folder";
|
||||
|
||||
// do not touch if it wasn't changed anyway
|
||||
// Do not touch if it wasn't changed.
|
||||
if (!(*fit)->metadata.wasChanged())
|
||||
continue;
|
||||
|
||||
// check if the file already exists in the XML
|
||||
// if it does, remove it before adding
|
||||
for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag))
|
||||
{
|
||||
// Check if the file already exists in the XML file.
|
||||
// If it does, remove the entry before adding it back.
|
||||
for (pugi::xml_node fileNode = root.child(tag); fileNode;
|
||||
fileNode = fileNode.next_sibling(tag)) {
|
||||
pugi::xml_node pathNode = fileNode.child("path");
|
||||
if(!pathNode)
|
||||
{
|
||||
if (!pathNode) {
|
||||
LOG(LogError) << "<" << tag << "> node contains no <path> child!";
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string nodePath = Utils::FileSystem::getCanonicalPath(Utils::FileSystem::resolveRelativePath(pathNode.text().get(), system->getStartPath(), true));
|
||||
std::string nodePath =Utils::FileSystem::getCanonicalPath(
|
||||
Utils::FileSystem::resolveRelativePath(pathNode.text().get(),
|
||||
system->getStartPath(), true));
|
||||
std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath());
|
||||
if(nodePath == gamePath)
|
||||
{
|
||||
// found it
|
||||
if (nodePath == gamePath) {
|
||||
// Found it
|
||||
root.remove_child(fileNode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// it was either removed or never existed to begin with; either way, we can add it now
|
||||
// It was either removed or never existed to begin with.
|
||||
// Either way, we can add it now.
|
||||
addFileDataNode(root, *fit, tag, system);
|
||||
++numUpdated;
|
||||
}
|
||||
|
||||
//now write the file
|
||||
|
||||
// Now write the file.
|
||||
if (numUpdated > 0) {
|
||||
const auto startTs = std::chrono::system_clock::now();
|
||||
|
||||
//make sure the folders leading up to this path exist (or the write will fail)
|
||||
// Make sure the folders leading up to this path exist (or the write will fail).
|
||||
std::string xmlWritePath(system->getGamelistPath(true));
|
||||
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath));
|
||||
|
||||
LOG(LogInfo) << "Added/Updated " << numUpdated << " entities in '" << xmlReadPath << "'";
|
||||
LOG(LogInfo) << "Added/Updated " << numUpdated <<
|
||||
" entities in '" << xmlReadPath << "'";
|
||||
|
||||
if (!doc.save_file(xmlWritePath.c_str())) {
|
||||
LOG(LogError) << "Error saving gamelist.xml to \"" << xmlWritePath << "\" (for system " << system->getName() << ")!";
|
||||
LOG(LogError) << "Error saving gamelist.xml to \"" <<
|
||||
xmlWritePath << "\" (for system " << system->getName() << ")!";
|
||||
}
|
||||
|
||||
const auto endTs = std::chrono::system_clock::now();
|
||||
LOG(LogInfo) << "Saved gamelist.xml for system \"" << system->getName() << "\" in " << std::chrono::duration_cast<std::chrono::milliseconds>(endTs - startTs).count() << " ms";
|
||||
LOG(LogInfo) << "Saved gamelist.xml for system \"" << system->getName() << "\" in " <<
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>
|
||||
(endTs - startTs).count() << " ms";
|
||||
}
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// Gamelist.h
|
||||
//
|
||||
// Parses and updates the gamelist.xml files.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_GAME_LIST_H
|
||||
#define ES_APP_GAME_LIST_H
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// MetaData.cpp
|
||||
//
|
||||
// Static data for default metadata values as well as functions
|
||||
// to read and write metadata from the gamelist files.
|
||||
//
|
||||
|
||||
#include "MetaData.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
@ -24,7 +31,8 @@ MetaDataDecl gameDecls[] = {
|
|||
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"}
|
||||
|
||||
};
|
||||
const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls + sizeof(gameDecls) / sizeof(gameDecls[0]));
|
||||
const std::vector<MetaDataDecl> gameMDD(gameDecls, gameDecls +
|
||||
sizeof(gameDecls) / sizeof(gameDecls[0]));
|
||||
|
||||
MetaDataDecl folderDecls[] = {
|
||||
{"name", MD_STRING, "", false, "name", "enter game name"},
|
||||
|
@ -37,12 +45,12 @@ MetaDataDecl folderDecls[] = {
|
|||
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre"},
|
||||
{"players", MD_INT, "1", false, "players", "enter number of players"}
|
||||
};
|
||||
const std::vector<MetaDataDecl> folderMDD(folderDecls, folderDecls + sizeof(folderDecls) / sizeof(folderDecls[0]));
|
||||
const std::vector<MetaDataDecl> folderMDD(folderDecls, folderDecls +
|
||||
sizeof(folderDecls) / sizeof(folderDecls[0]));
|
||||
|
||||
const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
switch(type) {
|
||||
case GAME_METADATA:
|
||||
return gameMDD;
|
||||
case FOLDER_METADATA:
|
||||
|
@ -53,36 +61,31 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
|
|||
return gameMDD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
MetaDataList::MetaDataList(MetaDataListType type)
|
||||
: mType(type), mWasChanged(false)
|
||||
: mType(type), mWasChanged(false)
|
||||
{
|
||||
const std::vector<MetaDataDecl>& mdd = getMDD();
|
||||
for(auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
|
||||
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
|
||||
set(iter->key, iter->defaultValue);
|
||||
}
|
||||
|
||||
|
||||
MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo)
|
||||
MetaDataList MetaDataList::createFromXML(MetaDataListType type,
|
||||
pugi::xml_node& node, const std::string& relativeTo)
|
||||
{
|
||||
MetaDataList mdl(type);
|
||||
|
||||
const std::vector<MetaDataDecl>& mdd = mdl.getMDD();
|
||||
|
||||
for(auto iter = mdd.cbegin(); iter != mdd.cend(); iter++)
|
||||
{
|
||||
for (auto iter = mdd.cbegin(); iter != mdd.cend(); iter++) {
|
||||
pugi::xml_node md = node.child(iter->key.c_str());
|
||||
if(md)
|
||||
{
|
||||
// if it's a path, resolve relative paths
|
||||
if (md) {
|
||||
// If it's a path, resolve relative paths.
|
||||
std::string value = md.text().get();
|
||||
if (iter->type == MD_PATH)
|
||||
{
|
||||
value = Utils::FileSystem::resolveRelativePath(value, relativeTo, true);
|
||||
}
|
||||
mdl.set(iter->key, value);
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
mdl.set(iter->key, iter->defaultValue);
|
||||
}
|
||||
}
|
||||
|
@ -90,21 +93,20 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node&
|
|||
return mdl;
|
||||
}
|
||||
|
||||
void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo) const
|
||||
void MetaDataList::appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
|
||||
const std::string& relativeTo) const
|
||||
{
|
||||
const std::vector<MetaDataDecl>& mdd = getMDD();
|
||||
|
||||
for(auto mddIter = mdd.cbegin(); mddIter != mdd.cend(); mddIter++)
|
||||
{
|
||||
for (auto mddIter = mdd.cbegin(); mddIter != mdd.cend(); mddIter++) {
|
||||
auto mapIter = mMap.find(mddIter->key);
|
||||
if(mapIter != mMap.cend())
|
||||
{
|
||||
// we have this value!
|
||||
// if it's just the default (and we ignore defaults), don't write it
|
||||
if(ignoreDefaults && mapIter->second == mddIter->defaultValue)
|
||||
if (mapIter != mMap.cend()) {
|
||||
// We have this value!
|
||||
// If it's just the default (and we ignore defaults), don't write it.
|
||||
if (ignoreDefaults && mapIter->second == mddIter->defaultValue)
|
||||
continue;
|
||||
|
||||
// try and make paths relative if we can
|
||||
// Try and make paths relative if we can.
|
||||
std::string value = mapIter->second;
|
||||
if (mddIter->type == MD_PATH)
|
||||
value = Utils::FileSystem::createRelativePath(value, relativeTo, true);
|
||||
|
@ -122,7 +124,12 @@ void MetaDataList::set(const std::string& key, const std::string& value)
|
|||
|
||||
const std::string& MetaDataList::get(const std::string& key) const
|
||||
{
|
||||
return mMap.at(key);
|
||||
// Check that the key actually exists, otherwise return empty string.
|
||||
if (mMap.count(key) > 0)
|
||||
return mMap.at(key);
|
||||
else
|
||||
|
||||
return mNoResult;
|
||||
}
|
||||
|
||||
int MetaDataList::getInt(const std::string& key) const
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// MetaData.h
|
||||
//
|
||||
// Static data for default metadata values as well as functions
|
||||
// to read and write metadata from the gamelist files.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_META_DATA_H
|
||||
#define ES_APP_META_DATA_H
|
||||
|
@ -7,35 +14,35 @@
|
|||
|
||||
namespace pugi { class xml_node; }
|
||||
|
||||
enum MetaDataType
|
||||
{
|
||||
//generic types
|
||||
enum MetaDataType {
|
||||
// Generic types
|
||||
MD_STRING,
|
||||
MD_INT,
|
||||
MD_FLOAT,
|
||||
MD_BOOL,
|
||||
|
||||
//specialized types
|
||||
// Specialized types
|
||||
MD_MULTILINE_STRING,
|
||||
MD_LAUNCHSTRING,
|
||||
MD_PATH,
|
||||
MD_RATING,
|
||||
MD_DATE,
|
||||
MD_TIME //used for lastplayed
|
||||
MD_TIME // Used for lastplayed
|
||||
};
|
||||
|
||||
struct MetaDataDecl
|
||||
{
|
||||
struct MetaDataDecl {
|
||||
std::string key;
|
||||
MetaDataType type;
|
||||
std::string defaultValue;
|
||||
bool isStatistic; //if true, ignore values for this metadata
|
||||
std::string displayName; // displayed as this in editors
|
||||
std::string displayPrompt; // phrase displayed in editors when prompted to enter value (currently only for strings)
|
||||
// If true, ignore values for this metadata.
|
||||
bool isStatistic;
|
||||
// Displayed as this in editors.
|
||||
std::string displayName;
|
||||
// Phrase displayed in editors when prompted to enter value (currently only for strings).
|
||||
std::string displayPrompt;
|
||||
};
|
||||
|
||||
enum MetaDataListType
|
||||
{
|
||||
enum MetaDataListType {
|
||||
GAME_METADATA,
|
||||
FOLDER_METADATA
|
||||
};
|
||||
|
@ -45,8 +52,10 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type);
|
|||
class MetaDataList
|
||||
{
|
||||
public:
|
||||
static MetaDataList createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo);
|
||||
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo) const;
|
||||
static MetaDataList createFromXML(MetaDataListType type,
|
||||
pugi::xml_node& node, const std::string& relativeTo);
|
||||
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
|
||||
const std::string& relativeTo) const;
|
||||
|
||||
MetaDataList(MetaDataListType type);
|
||||
|
||||
|
@ -65,6 +74,7 @@ public:
|
|||
private:
|
||||
MetaDataListType mType;
|
||||
std::map<std::string, std::string> mMap;
|
||||
std::string mNoResult = "";
|
||||
bool mWasChanged;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//
|
||||
// SystemData.cpp
|
||||
//
|
||||
// Provides data structures for the game systems and populates and indexes them based
|
||||
// on the configuration in es_systems.cfg as well as the presence of game ROM files.
|
||||
// Also provides functions to read and write to the gamelist files and to handle theme
|
||||
// loading.
|
||||
//
|
||||
|
||||
#include "SystemData.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
@ -18,31 +27,44 @@
|
|||
|
||||
std::vector<SystemData*> SystemData::sSystemVector;
|
||||
|
||||
SystemData::SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem) :
|
||||
mName(name), mFullName(fullName), mEnvData(envData), mThemeFolder(themeFolder), mIsCollectionSystem(CollectionSystem), mIsGameSystem(true)
|
||||
SystemData::SystemData(
|
||||
const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem)
|
||||
: mName(name),
|
||||
mFullName(fullName),
|
||||
mEnvData(envData),
|
||||
mThemeFolder(themeFolder),
|
||||
mIsCollectionSystem(CollectionSystem),
|
||||
mIsGameSystem(true)
|
||||
{
|
||||
mFilterIndex = new FileFilterIndex();
|
||||
|
||||
// if it's an actual system, initialize it, if not, just create the data structure
|
||||
if(!CollectionSystem)
|
||||
{
|
||||
// If it's an actual system, initialize it, if not, just create the data structure.
|
||||
if (!CollectionSystem) {
|
||||
mRootFolder = new FileData(FOLDER, mEnvData->mStartPath, mEnvData, this);
|
||||
mRootFolder->metadata.set("name", mFullName);
|
||||
|
||||
if(!Settings::getInstance()->getBool("ParseGamelistOnly"))
|
||||
if (!Settings::getInstance()->getBool("ParseGamelistOnly"))
|
||||
populateFolder(mRootFolder);
|
||||
|
||||
if(!Settings::getInstance()->getBool("IgnoreGamelist"))
|
||||
if (!Settings::getInstance()->getBool("IgnoreGamelist"))
|
||||
parseGamelist(this);
|
||||
|
||||
mRootFolder->sort(FileSorts::SortTypes.at(0));
|
||||
setupSystemSortType(mRootFolder);
|
||||
|
||||
mRootFolder->sort(getSortTypeFromString(mRootFolder->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
|
||||
indexAllGameFilters(mRootFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// virtual systems are updated afterwards, we're just creating the data structure
|
||||
else {
|
||||
// Virtual systems are updated afterwards by CollectionSystemManager.
|
||||
// We're just creating the data structure here.
|
||||
mRootFolder = new FileData(FOLDER, "" + name, mEnvData, this);
|
||||
setupSystemSortType(mRootFolder);
|
||||
}
|
||||
setIsGameSystemStatus();
|
||||
loadTheme();
|
||||
|
@ -50,7 +72,7 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, Sys
|
|||
|
||||
SystemData::~SystemData()
|
||||
{
|
||||
if(Settings::getInstance()->getString("SaveGamelistsMode") == "on exit")
|
||||
if (Settings::getInstance()->getString("SaveGamelistsMode") == "on exit")
|
||||
writeMetaData();
|
||||
|
||||
delete mRootFolder;
|
||||
|
@ -59,27 +81,25 @@ SystemData::~SystemData()
|
|||
|
||||
void SystemData::setIsGameSystemStatus()
|
||||
{
|
||||
// we exclude non-game systems from specific operations (i.e. the "RetroPie" system, at least)
|
||||
// if/when there are more in the future, maybe this can be a more complex method, with a proper list
|
||||
// but for now a simple string comparison is more performant
|
||||
// We exclude non-game systems from specific operations (i.e. the "RetroPie" system, at least).
|
||||
// If/when there are more in the future, maybe this can be a more complex method, with a proper
|
||||
// list but for now a simple string comparison is more performant.
|
||||
mIsGameSystem = (mName != "retropie");
|
||||
}
|
||||
|
||||
void SystemData::populateFolder(FileData* folder)
|
||||
{
|
||||
const std::string& folderPath = folder->getPath();
|
||||
if(!Utils::FileSystem::isDirectory(folderPath))
|
||||
{
|
||||
if (!Utils::FileSystem::isDirectory(folderPath)) {
|
||||
LOG(LogWarning) << "Error - folder with path \"" << folderPath << "\" is not a directory!";
|
||||
return;
|
||||
}
|
||||
|
||||
//make sure that this isn't a symlink to a thing we already have
|
||||
if(Utils::FileSystem::isSymlink(folderPath))
|
||||
{
|
||||
//if this symlink resolves to somewhere that's at the beginning of our path, it's gonna recurse
|
||||
if(folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0)
|
||||
{
|
||||
// Make sure that this isn't a symlink to an object we already have.
|
||||
if (Utils::FileSystem::isSymlink(folderPath)) {
|
||||
// If this symlink resolves to somewhere that's at the beginning of our
|
||||
// path, it's going to create a recursive loop. Make sure to avoid this.
|
||||
if (folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0) {
|
||||
LOG(LogWarning) << "Skipping infinitely recursive symlink \"" << folderPath << "\"";
|
||||
return;
|
||||
}
|
||||
|
@ -90,42 +110,42 @@ void SystemData::populateFolder(FileData* folder)
|
|||
bool isGame;
|
||||
bool showHidden = Settings::getInstance()->getBool("ShowHiddenFiles");
|
||||
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(folderPath);
|
||||
for(Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin(); it != dirContent.cend(); ++it)
|
||||
{
|
||||
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
|
||||
it != dirContent.cend(); ++it) {
|
||||
filePath = *it;
|
||||
|
||||
// skip hidden files and folders
|
||||
if(!showHidden && Utils::FileSystem::isHidden(filePath))
|
||||
// Skip hidden files and folders.
|
||||
if (!showHidden && Utils::FileSystem::isHidden(filePath))
|
||||
continue;
|
||||
|
||||
//this is a little complicated because we allow a list of extensions to be defined (delimited with a space)
|
||||
//we first get the extension of the file itself:
|
||||
// This is a little complicated because we allow a list
|
||||
// of extensions to be defined (delimited with a space).
|
||||
// We first get the extension of the file itself:
|
||||
extension = Utils::FileSystem::getExtension(filePath);
|
||||
|
||||
//fyi, folders *can* also match the extension and be added as games - this is mostly just to support higan
|
||||
//see issue #75: https://github.com/Aloshi/EmulationStation/issues/75
|
||||
// FYI, folders *can* also match the extension and be added as games.
|
||||
// This is mostly just to support higan.
|
||||
// See issue #75: https://github.com/Aloshi/EmulationStation/issues/75
|
||||
|
||||
isGame = false;
|
||||
if(std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(), extension) != mEnvData->mSearchExtensions.cend())
|
||||
{
|
||||
if (std::find(mEnvData->mSearchExtensions.cbegin(), mEnvData->mSearchExtensions.cend(),
|
||||
extension) != mEnvData->mSearchExtensions.cend()) {
|
||||
FileData* newGame = new FileData(GAME, filePath, mEnvData, this);
|
||||
|
||||
// preventing new arcade assets to be added
|
||||
if(!newGame->isArcadeAsset())
|
||||
{
|
||||
// Prevent new arcade assets from being added.
|
||||
if (!newGame->isArcadeAsset()) {
|
||||
folder->addChild(newGame);
|
||||
isGame = true;
|
||||
}
|
||||
}
|
||||
|
||||
//add directories that also do not match an extension as folders
|
||||
if(!isGame && Utils::FileSystem::isDirectory(filePath))
|
||||
{
|
||||
// Add directories that also do not match an extension as folders.
|
||||
if (!isGame && Utils::FileSystem::isDirectory(filePath)) {
|
||||
FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this);
|
||||
populateFolder(newFolder);
|
||||
|
||||
//ignore folders that do not contain games
|
||||
if(newFolder->getChildrenByFilename().size() == 0)
|
||||
// Ignore folders that do not contain games.
|
||||
if (newFolder->getChildrenByFilename().size() == 0)
|
||||
delete newFolder;
|
||||
else
|
||||
folder->addChild(newFolder);
|
||||
|
@ -137,12 +157,17 @@ void SystemData::indexAllGameFilters(const FileData* folder)
|
|||
{
|
||||
const std::vector<FileData*>& children = folder->getChildren();
|
||||
|
||||
for(std::vector<FileData*>::const_iterator it = children.cbegin(); it != children.cend(); ++it)
|
||||
{
|
||||
switch((*it)->getType())
|
||||
{
|
||||
case GAME: { mFilterIndex->addToIndex(*it); } break;
|
||||
case FOLDER: { indexAllGameFilters(*it); } break;
|
||||
for (std::vector<FileData*>::const_iterator it = children.cbegin();
|
||||
it != children.cend(); ++it) {
|
||||
switch ((*it)->getType()) {
|
||||
case GAME: {
|
||||
mFilterIndex->addToIndex(*it);
|
||||
}
|
||||
break;
|
||||
case FOLDER: {
|
||||
indexAllGameFilters(*it);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +178,7 @@ std::vector<std::string> readList(const std::string& str, const char* delims = "
|
|||
|
||||
size_t prevOff = str.find_first_not_of(delims, 0);
|
||||
size_t off = str.find_first_of(delims, prevOff);
|
||||
while(off != std::string::npos || prevOff != std::string::npos)
|
||||
{
|
||||
while (off != std::string::npos || prevOff != std::string::npos) {
|
||||
ret.push_back(str.substr(prevOff, off - prevOff));
|
||||
|
||||
prevOff = str.find_first_not_of(delims, off);
|
||||
|
@ -164,7 +188,7 @@ std::vector<std::string> readList(const std::string& str, const char* delims = "
|
|||
return ret;
|
||||
}
|
||||
|
||||
//creates systems from information located in a config file
|
||||
// Creates systems from information located in a config file.
|
||||
bool SystemData::loadConfig()
|
||||
{
|
||||
deleteSystems();
|
||||
|
@ -173,8 +197,7 @@ bool SystemData::loadConfig()
|
|||
|
||||
LOG(LogInfo) << "Loading system config file " << path << "...";
|
||||
|
||||
if(!Utils::FileSystem::exists(path))
|
||||
{
|
||||
if (!Utils::FileSystem::exists(path)) {
|
||||
LOG(LogError) << "es_systems.cfg file does not exist!";
|
||||
writeExampleConfig(getConfigPath(true));
|
||||
return false;
|
||||
|
@ -183,80 +206,78 @@ bool SystemData::loadConfig()
|
|||
pugi::xml_document doc;
|
||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
||||
|
||||
if(!res)
|
||||
{
|
||||
if (!res) {
|
||||
LOG(LogError) << "Could not parse es_systems.cfg file!";
|
||||
LOG(LogError) << res.description();
|
||||
return false;
|
||||
}
|
||||
|
||||
//actually read the file
|
||||
// Actually read the file.
|
||||
pugi::xml_node systemList = doc.child("systemList");
|
||||
|
||||
if(!systemList)
|
||||
{
|
||||
if (!systemList) {
|
||||
LOG(LogError) << "es_systems.cfg is missing the <systemList> tag!";
|
||||
return false;
|
||||
}
|
||||
|
||||
for(pugi::xml_node system = systemList.child("system"); system; system = system.next_sibling("system"))
|
||||
{
|
||||
for (pugi::xml_node system = systemList.child("system"); system;
|
||||
system = system.next_sibling("system")) {
|
||||
std::string name, fullname, path, cmd, themeFolder;
|
||||
|
||||
name = system.child("name").text().get();
|
||||
fullname = system.child("fullname").text().get();
|
||||
path = system.child("path").text().get();
|
||||
|
||||
// convert extensions list from a string into a vector of strings
|
||||
// Convert extensions list from a string into a vector of strings.
|
||||
std::vector<std::string> extensions = readList(system.child("extension").text().get());
|
||||
|
||||
cmd = system.child("command").text().get();
|
||||
|
||||
// platform id list
|
||||
// Platform ID list
|
||||
const char* platformList = system.child("platform").text().get();
|
||||
std::vector<std::string> platformStrs = readList(platformList);
|
||||
std::vector<PlatformIds::PlatformId> platformIds;
|
||||
for(auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++)
|
||||
{
|
||||
for (auto it = platformStrs.cbegin(); it != platformStrs.cend(); it++) {
|
||||
const char* str = it->c_str();
|
||||
PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str);
|
||||
|
||||
if(platformId == PlatformIds::PLATFORM_IGNORE)
|
||||
{
|
||||
// when platform is ignore, do not allow other platforms
|
||||
if (platformId == PlatformIds::PLATFORM_IGNORE) {
|
||||
// When platform is PLATFORM_IGNORE, do not allow other platforms.
|
||||
platformIds.clear();
|
||||
platformIds.push_back(platformId);
|
||||
break;
|
||||
}
|
||||
|
||||
// if there appears to be an actual platform ID supplied but it didn't match the list, warn
|
||||
if(str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
|
||||
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")";
|
||||
else if(platformId != PlatformIds::PLATFORM_UNKNOWN)
|
||||
// If there appears to be an actual platform ID supplied
|
||||
// but it didn't match the list, generate a warning.
|
||||
if (str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
|
||||
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \""
|
||||
<< str << "\" from list \"" << platformList << "\")";
|
||||
else if (platformId != PlatformIds::PLATFORM_UNKNOWN)
|
||||
platformIds.push_back(platformId);
|
||||
}
|
||||
|
||||
// theme folder
|
||||
// Theme folder.
|
||||
themeFolder = system.child("theme").text().as_string(name.c_str());
|
||||
|
||||
//validate
|
||||
if(name.empty() || path.empty() || extensions.empty() || cmd.empty())
|
||||
{
|
||||
LOG(LogError) << "System \"" << name << "\" is missing name, path, extension, or command!";
|
||||
// Validate.
|
||||
if (name.empty() || path.empty() || extensions.empty() || cmd.empty()) {
|
||||
LOG(LogError) << "System \"" << name <<
|
||||
"\" is missing name, path, extension, or command!";
|
||||
continue;
|
||||
}
|
||||
|
||||
//convert path to generic directory seperators
|
||||
// Convert path to generic directory seperators.
|
||||
path = Utils::FileSystem::getGenericPath(path);
|
||||
|
||||
//expand home symbol if the startpath contains ~
|
||||
if(path[0] == '~')
|
||||
// Expand home symbol if the startpath contains ~
|
||||
if (path[0] == '~')
|
||||
{
|
||||
path.erase(0, 1);
|
||||
path.insert(0, Utils::FileSystem::getHomePath());
|
||||
}
|
||||
|
||||
//create the system runtime environment data
|
||||
// Create the system runtime environment data.
|
||||
SystemEnvironmentData* envData = new SystemEnvironmentData;
|
||||
envData->mStartPath = path;
|
||||
envData->mSearchExtensions = extensions;
|
||||
|
@ -264,11 +285,11 @@ bool SystemData::loadConfig()
|
|||
envData->mPlatformIds = platformIds;
|
||||
|
||||
SystemData* newSys = new SystemData(name, fullname, envData, themeFolder);
|
||||
if(newSys->getRootFolder()->getChildrenByFilename().size() == 0)
|
||||
{
|
||||
if (newSys->getRootFolder()->getChildrenByFilename().size() == 0) {
|
||||
LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it.";
|
||||
delete newSys;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
sSystemVector.push_back(newSys);
|
||||
}
|
||||
}
|
||||
|
@ -325,17 +346,16 @@ void SystemData::writeExampleConfig(const std::string& path)
|
|||
|
||||
void SystemData::deleteSystems()
|
||||
{
|
||||
for(unsigned int i = 0; i < sSystemVector.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < sSystemVector.size(); i++)
|
||||
delete sSystemVector.at(i);
|
||||
}
|
||||
|
||||
sSystemVector.clear();
|
||||
}
|
||||
|
||||
std::string SystemData::getConfigPath(bool forWrite)
|
||||
{
|
||||
std::string path = Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg";
|
||||
if(forWrite || Utils::FileSystem::exists(path))
|
||||
if (forWrite || Utils::FileSystem::exists(path))
|
||||
return path;
|
||||
|
||||
return "/etc/emulationstation/es_systems.cfg";
|
||||
|
@ -352,12 +372,13 @@ SystemData* SystemData::getNext() const
|
|||
{
|
||||
std::vector<SystemData*>::const_iterator it = getIterator();
|
||||
|
||||
// As we are starting in a valid gamelistview, this will
|
||||
// always succeed, even if we have to come full circle.
|
||||
do {
|
||||
it++;
|
||||
if (it == sSystemVector.cend())
|
||||
it = sSystemVector.cbegin();
|
||||
} while (!(*it)->isVisible());
|
||||
// as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle.
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
@ -366,12 +387,13 @@ SystemData* SystemData::getPrev() const
|
|||
{
|
||||
std::vector<SystemData*>::const_reverse_iterator it = getRevIterator();
|
||||
|
||||
// As we are starting in a valid gamelistview, this will
|
||||
// always succeed, even if we have to come full circle.
|
||||
do {
|
||||
it++;
|
||||
if (it == sSystemVector.crend())
|
||||
it = sSystemVector.crbegin();
|
||||
} while (!(*it)->isVisible());
|
||||
// as we are starting in a valid gamelistview, this will always succeed, even if we have to come full circle.
|
||||
|
||||
return *it;
|
||||
}
|
||||
|
@ -381,13 +403,15 @@ std::string SystemData::getGamelistPath(bool forWrite) const
|
|||
std::string filePath;
|
||||
|
||||
filePath = mRootFolder->getPath() + "/gamelist.xml";
|
||||
if(Utils::FileSystem::exists(filePath))
|
||||
if (Utils::FileSystem::exists(filePath))
|
||||
return filePath;
|
||||
|
||||
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName + "/gamelist.xml";
|
||||
if(forWrite) // make sure the directory exists if we're going to write to it, or crashes will happen
|
||||
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" +
|
||||
mName + "/gamelist.xml";
|
||||
// Make sure the directory exists if we're going to write to it, or crashes will happen.
|
||||
if (forWrite)
|
||||
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(filePath));
|
||||
if(forWrite || Utils::FileSystem::exists(filePath))
|
||||
if (forWrite || Utils::FileSystem::exists(filePath))
|
||||
return filePath;
|
||||
|
||||
return "/etc/emulationstation/gamelists/" + mName + "/gamelist.xml";
|
||||
|
@ -395,24 +419,25 @@ std::string SystemData::getGamelistPath(bool forWrite) const
|
|||
|
||||
std::string SystemData::getThemePath() const
|
||||
{
|
||||
// where we check for themes, in order:
|
||||
// Locations where we check for themes, in the following order:
|
||||
// 1. [SYSTEM_PATH]/theme.xml
|
||||
// 2. system theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml
|
||||
// 3. default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml
|
||||
// 2. System theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/theme.xml
|
||||
// 3. Default system theme from currently selected theme set [CURRENT_THEME_PATH]/theme.xml
|
||||
|
||||
// first, check game folder
|
||||
// First, check game folder.
|
||||
std::string localThemePath = mRootFolder->getPath() + "/theme.xml";
|
||||
if(Utils::FileSystem::exists(localThemePath))
|
||||
if (Utils::FileSystem::exists(localThemePath))
|
||||
return localThemePath;
|
||||
|
||||
// not in game folder, try system theme in theme sets
|
||||
// Not in game folder, try system theme in theme sets.
|
||||
localThemePath = ThemeData::getThemeFromCurrentSet(mThemeFolder);
|
||||
|
||||
if (Utils::FileSystem::exists(localThemePath))
|
||||
return localThemePath;
|
||||
|
||||
// not system theme, try default system theme in theme set
|
||||
localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml";
|
||||
// Not system theme, try default system theme in theme set.
|
||||
localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) +
|
||||
"/theme.xml";
|
||||
|
||||
return localThemePath;
|
||||
}
|
||||
|
@ -429,32 +454,27 @@ unsigned int SystemData::getGameCount() const
|
|||
|
||||
SystemData* SystemData::getRandomSystem()
|
||||
{
|
||||
// this is a bit brute force. It might be more efficient to just to a while (!gameSystem) do random again...
|
||||
// This is a bit brute force.
|
||||
// It might be more efficient to just do a while (!gameSystem) do random again...
|
||||
unsigned int total = 0;
|
||||
for(auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++)
|
||||
{
|
||||
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++) {
|
||||
if ((*it)->isGameSystem())
|
||||
total ++;
|
||||
total++;
|
||||
}
|
||||
|
||||
// get random number in range
|
||||
// Get a random number in range.
|
||||
int target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
|
||||
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++)
|
||||
{
|
||||
if ((*it)->isGameSystem())
|
||||
{
|
||||
if ((*it)->isGameSystem()) {
|
||||
if (target > 0)
|
||||
{
|
||||
target--;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (*it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we end up here, there is no valid system
|
||||
// If we end up here, there is no valid system.
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -463,10 +483,12 @@ FileData* SystemData::getRandomGame()
|
|||
std::vector<FileData*> list = mRootFolder->getFilesRecursive(GAME, true);
|
||||
unsigned int total = (int)list.size();
|
||||
int target = 0;
|
||||
// get random number in range
|
||||
|
||||
// Get a random number in range.
|
||||
if (total == 0)
|
||||
return NULL;
|
||||
target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
|
||||
|
||||
return list.at(target);
|
||||
}
|
||||
|
||||
|
@ -481,36 +503,54 @@ void SystemData::loadTheme()
|
|||
|
||||
std::string path = getThemePath();
|
||||
|
||||
if(!Utils::FileSystem::exists(path)) // no theme available for this platform
|
||||
if (!Utils::FileSystem::exists(path)) // No theme available for this platform.
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
// build map with system variables for theme to use,
|
||||
try {
|
||||
// Build map with system variables for theme to use.
|
||||
std::map<std::string, std::string> sysData;
|
||||
sysData.insert(std::pair<std::string, std::string>("system.name", getName()));
|
||||
sysData.insert(std::pair<std::string, std::string>("system.theme", getThemeFolder()));
|
||||
sysData.insert(std::pair<std::string, std::string>("system.fullName", getFullName()));
|
||||
|
||||
mTheme->loadFile(sysData, path);
|
||||
} catch(ThemeException& e)
|
||||
{
|
||||
}
|
||||
catch (ThemeException& e) {
|
||||
LOG(LogError) << e.what();
|
||||
mTheme = std::make_shared<ThemeData>(); // reset to empty
|
||||
mTheme = std::make_shared<ThemeData>(); // Reset to empty.
|
||||
}
|
||||
}
|
||||
|
||||
void SystemData::writeMetaData() {
|
||||
if(Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem)
|
||||
if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem)
|
||||
return;
|
||||
|
||||
//save changed game data back to xml
|
||||
// Save changed game data back to xml.
|
||||
updateGamelist(this);
|
||||
}
|
||||
|
||||
void SystemData::onMetaDataSavePoint() {
|
||||
if(Settings::getInstance()->getString("SaveGamelistsMode") != "always")
|
||||
if (Settings::getInstance()->getString("SaveGamelistsMode") != "always")
|
||||
return;
|
||||
|
||||
writeMetaData();
|
||||
}
|
||||
|
||||
void SystemData::setupSystemSortType(FileData* mRootFolder)
|
||||
{
|
||||
// If DefaultSortOrder is set to something, check that it is actually a valid value.
|
||||
if (Settings::getInstance()->getString("DefaultSortOrder") != "") {
|
||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
||||
if (FileSorts::SortTypes.at(i).description ==
|
||||
Settings::getInstance()->getString("DefaultSortOrder")) {
|
||||
mRootFolder->setSortTypeString(Settings::getInstance()->
|
||||
getString("DefaultSortOrder"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If no valid sort type was defined in the configuration
|
||||
// file, set sorting to "filename, ascending".
|
||||
if (mRootFolder->getSortTypeString() == "")
|
||||
mRootFolder->setSortTypeString(FileSorts::SortTypes.at(0).description);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//
|
||||
// SystemData.h
|
||||
//
|
||||
// Provides data structures for the game systems and populates and indexes them based
|
||||
// on the configuration in es_systems.cfg as well as the presence of game ROM files.
|
||||
// Also provides functions to read and write to the gamelist files and to handle theme
|
||||
// loading.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_SYSTEM_DATA_H
|
||||
#define ES_APP_SYSTEM_DATA_H
|
||||
|
@ -23,18 +32,27 @@ struct SystemEnvironmentData
|
|||
class SystemData
|
||||
{
|
||||
public:
|
||||
SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem = false);
|
||||
SystemData(const std::string& name,
|
||||
const std::string& fullName,
|
||||
SystemEnvironmentData* envData,
|
||||
const std::string& themeFolder,
|
||||
bool CollectionSystem = false);
|
||||
|
||||
~SystemData();
|
||||
|
||||
inline FileData* getRootFolder() const { return mRootFolder; };
|
||||
inline const std::string& getName() const { return mName; }
|
||||
inline const std::string& getFullName() const { return mFullName; }
|
||||
inline const std::string& getStartPath() const { return mEnvData->mStartPath; }
|
||||
inline const std::vector<std::string>& getExtensions() const { return mEnvData->mSearchExtensions; }
|
||||
inline const std::vector<std::string>& getExtensions() const
|
||||
{ return mEnvData->mSearchExtensions; }
|
||||
inline const std::string& getThemeFolder() const { return mThemeFolder; }
|
||||
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
|
||||
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const { return mEnvData->mPlatformIds; }
|
||||
inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) != mEnvData->mPlatformIds.cend(); }
|
||||
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const
|
||||
{ return mEnvData->mPlatformIds; }
|
||||
inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false;
|
||||
return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id)
|
||||
!= mEnvData->mPlatformIds.cend(); }
|
||||
|
||||
inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
|
||||
|
||||
|
@ -46,14 +64,21 @@ public:
|
|||
unsigned int getDisplayedGameCount() const;
|
||||
|
||||
static void deleteSystems();
|
||||
static bool loadConfig(); //Load the system config file at getConfigPath(). Returns true if no errors were encountered. An example will be written if the file doesn't exist.
|
||||
// Load the system config file at getConfigPath().
|
||||
// Returns true if no errors were encountered.
|
||||
// An example will be written if the file doesn't exist.
|
||||
static bool loadConfig();
|
||||
static void writeExampleConfig(const std::string& path);
|
||||
static std::string getConfigPath(bool forWrite); // if forWrite, will only return ~/.emulationstation/es_systems.cfg, never /etc/emulationstation/es_systems.cfg
|
||||
// If forWrite, will only return ~/.emulationstation/es_systems.cfg,
|
||||
// never /etc/emulationstation/es_systems.cfg.
|
||||
static std::string getConfigPath(bool forWrite);
|
||||
|
||||
static std::vector<SystemData*> sSystemVector;
|
||||
|
||||
inline std::vector<SystemData*>::const_iterator getIterator() const { return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); };
|
||||
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); };
|
||||
inline std::vector<SystemData*>::const_iterator getIterator() const
|
||||
{ return std::find(sSystemVector.cbegin(), sSystemVector.cend(), this); };
|
||||
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const
|
||||
{ return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); };
|
||||
inline bool isCollection() { return mIsCollectionSystem; };
|
||||
inline bool isGameSystem() { return mIsGameSystem; };
|
||||
|
||||
|
@ -70,6 +95,8 @@ public:
|
|||
FileFilterIndex* getIndex() { return mFilterIndex; };
|
||||
void onMetaDataSavePoint();
|
||||
|
||||
void setupSystemSortType(FileData* mRootFolder);
|
||||
|
||||
private:
|
||||
bool mIsCollectionSystem;
|
||||
bool mIsGameSystem;
|
||||
|
|
|
@ -71,9 +71,9 @@ void GuiCollectionSystemsOptions::initializeMenu()
|
|||
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
|
||||
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
|
||||
|
||||
sortAllSystemsSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
sortAllSystemsSwitch->setState(Settings::getInstance()->getBool("SortAllSystems"));
|
||||
mMenu.addWithLabel("SORT CUSTOM COLLECTIONS AND SYSTEMS", sortAllSystemsSwitch);
|
||||
sortFavFirstCustomSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom"));
|
||||
mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch);
|
||||
|
||||
toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow);
|
||||
toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo"));
|
||||
|
@ -170,8 +170,8 @@ void GuiCollectionSystemsOptions::applySettings()
|
|||
std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto");
|
||||
std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects());
|
||||
std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom");
|
||||
bool outSort = sortAllSystemsSwitch->getState();
|
||||
bool prevSort = Settings::getInstance()->getBool("SortAllSystems");
|
||||
bool outSort = sortFavFirstCustomSwitch->getState();
|
||||
bool prevSort = Settings::getInstance()->getBool("FavFirstCustom");
|
||||
bool outBundle = bundleCustomCollections->getState();
|
||||
bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem");
|
||||
bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo");
|
||||
|
@ -189,7 +189,7 @@ void GuiCollectionSystemsOptions::updateSettings(std::string newAutoSettings, st
|
|||
{
|
||||
Settings::getInstance()->setString("CollectionSystemsAuto", newAutoSettings);
|
||||
Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings);
|
||||
Settings::getInstance()->setBool("SortAllSystems", sortAllSystemsSwitch->getState());
|
||||
Settings::getInstance()->setBool("FavFirstCustom", sortFavFirstCustomSwitch->getState());
|
||||
Settings::getInstance()->setBool("UseCustomCollectionsSystem", bundleCustomCollections->getState());
|
||||
Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState());
|
||||
Settings::getInstance()->saveFile();
|
||||
|
|
|
@ -28,8 +28,8 @@ private:
|
|||
void exitEditMode();
|
||||
std::shared_ptr< OptionListComponent<std::string> > autoOptionList;
|
||||
std::shared_ptr< OptionListComponent<std::string> > customOptionList;
|
||||
std::shared_ptr<SwitchComponent> sortAllSystemsSwitch;
|
||||
std::shared_ptr<SwitchComponent> bundleCustomCollections;
|
||||
std::shared_ptr<SwitchComponent> sortFavFirstCustomSwitch;
|
||||
std::shared_ptr<SwitchComponent> toggleSystemNameInCollections;
|
||||
MenuComponent mMenu;
|
||||
SystemData* mSystem;
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// GuiGamelistOptions.cpp
|
||||
//
|
||||
// Gamelist options menu for the 'Jump to...' quick selector,
|
||||
// game sorting, game filters, and metadata edit.
|
||||
//
|
||||
|
||||
#include "GuiGamelistOptions.h"
|
||||
|
||||
#include "guis/GuiGamelistFilter.h"
|
||||
|
@ -12,43 +19,50 @@
|
|||
#include "SystemData.h"
|
||||
#include "Sound.h"
|
||||
|
||||
GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window),
|
||||
mSystem(system), mMenu(window, "OPTIONS"), fromPlaceholder(false), mFiltersChanged(false)
|
||||
GuiGamelistOptions::GuiGamelistOptions(
|
||||
Window* window,
|
||||
SystemData* system)
|
||||
: GuiComponent(window),
|
||||
mSystem(system),
|
||||
mMenu(window, "OPTIONS"),
|
||||
fromPlaceholder(false),
|
||||
mFiltersChanged(false)
|
||||
{
|
||||
addChild(&mMenu);
|
||||
|
||||
// check it's not a placeholder folder - if it is, only show "Filter Options"
|
||||
// Check that it's not a placeholder folder - if it is, only show "Filter Options".
|
||||
FileData* file = getGamelist()->getCursor();
|
||||
fromPlaceholder = file->isPlaceHolder();
|
||||
ComponentListRow row;
|
||||
|
||||
// Read the applicable favorite sorting setting depending on whether the
|
||||
// system is a custom collection or not.
|
||||
if (CollectionSystemManager::get()->getIsCustomCollection(file->getSystem()))
|
||||
mFavoritesSorting = Settings::getInstance()->getBool("FavFirstCustom");
|
||||
else
|
||||
mFavoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||
|
||||
if (!fromPlaceholder) {
|
||||
// jump to letter
|
||||
// Jump to letter.
|
||||
row.elements.clear();
|
||||
|
||||
// define supported character range
|
||||
// this range includes all numbers, capital letters, and most reasonable symbols
|
||||
// Define supported character range.
|
||||
// This range includes all numbers, capital letters, and most reasonable symbols.
|
||||
char startChar = '!';
|
||||
char endChar = '_';
|
||||
|
||||
char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]);
|
||||
if(curChar < startChar || curChar > endChar)
|
||||
if (curChar < startChar || curChar > endChar)
|
||||
curChar = startChar;
|
||||
|
||||
mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO...", false);
|
||||
|
||||
if(Settings::getInstance()->getBool("FavoritesFirst") && system->getName() != "favorites" && system->getName() != "recent")
|
||||
{
|
||||
// set firstFavorite to the numerical entry of the first favorite game in the list
|
||||
// if no favorites are found set it to -1
|
||||
findFirstFavorite();
|
||||
|
||||
// if the currently selected game is a favorite, set curChar to 0 so we don't get two current positions
|
||||
if(getGamelist()->getCursor()->getFavorite())
|
||||
curChar = 0;
|
||||
|
||||
if (firstFavorite != -1)
|
||||
{
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent") {
|
||||
// Check whether the first game in the list is a favorite, if it's
|
||||
// not, then there are no favorites currently visible in this gamelist.
|
||||
if (getGamelist()->getCursor()->getParent()->getChildrenListToDisplay()[0]->
|
||||
getFavorite()) {
|
||||
if (getGamelist()->getCursor()->getFavorite())
|
||||
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1);
|
||||
else
|
||||
|
@ -56,117 +70,147 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
|
|||
}
|
||||
}
|
||||
|
||||
for (char c = startChar; c <= endChar; c++)
|
||||
{
|
||||
// check if c is a valid first letter in current list
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
for (auto file : files)
|
||||
{
|
||||
for (char c = startChar; c <= endChar; c++) {
|
||||
// Check if c is a valid first letter in the current list.
|
||||
const std::vector<FileData*>& files =
|
||||
getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
for (auto file : files) {
|
||||
char candidate = (char)toupper(file->getSortName()[0]);
|
||||
if (c == candidate)
|
||||
{
|
||||
// if the game is a favorite, continue to the next entry in the list
|
||||
if (firstFavorite != -1 && file->getFavorite())
|
||||
if (c == candidate) {
|
||||
// If the game is a favorite, continue to the next entry in the list.
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent" && file->getFavorite())
|
||||
continue;
|
||||
|
||||
mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar);
|
||||
// If the currently selected game is a favorite, set the character
|
||||
// as not selected so we don't get two current positions.
|
||||
if (mFavoritesSorting && system->getName() != "favorites" &&
|
||||
system->getName() != "recent" &&
|
||||
getGamelist()->getCursor()->getFavorite())
|
||||
mJumpToLetterList->add(std::string(1, c), std::string(1, c), 0);
|
||||
else
|
||||
mJumpToLetterList->add(std::string(1, c), std::string(1, c), c == curChar);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "JUMP TO...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(mJumpToLetterList, false);
|
||||
row.input_handler = [&](InputConfig* config, Input input) {
|
||||
if(config->isMappedTo("a", input) && input.value)
|
||||
{
|
||||
if(mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
||||
{
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
jumpToFirstFavorite();
|
||||
return true;
|
||||
}
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
jumpToLetter();
|
||||
if (config->isMappedTo("a", input) && input.value) {
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
|
||||
jumpToFirstRow();
|
||||
else
|
||||
jumpToLetter();
|
||||
|
||||
return true;
|
||||
}
|
||||
else if(mJumpToLetterList->input(config, input))
|
||||
{
|
||||
else if (mJumpToLetterList->input(config, input)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
mMenu.addRow(row);
|
||||
if (system->getName() != "recent")
|
||||
mMenu.addRow(row);
|
||||
|
||||
// sort list by
|
||||
// Sort list by selected sort type (persistent throughout the program session).
|
||||
mListSort = std::make_shared<SortList>(mWindow, "SORT GAMES BY", false);
|
||||
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++)
|
||||
{
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent
|
||||
}
|
||||
FileData* root = mSystem->getRootFolder();
|
||||
std::string sortType = root->getSortTypeString();
|
||||
|
||||
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
||||
for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
|
||||
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
|
||||
if (sort.description == sortType)
|
||||
mListSort->add(sort.description, &sort, 1);
|
||||
else
|
||||
mListSort->add(sort.description, &sort, 0);
|
||||
}
|
||||
// Don't show the sort type option if the gamelist type is recent/last played.
|
||||
if (system->getName() != "recent")
|
||||
mMenu.addWithLabel("SORT GAMES BY", mListSort);
|
||||
}
|
||||
// show filtered menu
|
||||
if(!Settings::getInstance()->getBool("ForceDisableFilters"))
|
||||
{
|
||||
|
||||
// Show filtered menu.
|
||||
if (system->getName() != "recent" && !Settings::getInstance()->getBool("ForceDisableFilters")) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "FILTER GAMELIST", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
std::map<std::string, CollectionSystemData> customCollections = CollectionSystemManager::get()->getCustomCollectionSystems();
|
||||
std::map<std::string, CollectionSystemData> customCollections =
|
||||
CollectionSystemManager::get()->getCustomCollectionSystems();
|
||||
|
||||
if(UIModeController::getInstance()->isUIModeFull() &&
|
||||
((customCollections.find(system->getName()) != customCollections.cend() && CollectionSystemManager::get()->getEditingCollection() != system->getName()) ||
|
||||
CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName()))
|
||||
{
|
||||
if (UIModeController::getInstance()->isUIModeFull() &&
|
||||
((customCollections.find(system->getName()) != customCollections.cend() &&
|
||||
CollectionSystemManager::get()->getEditingCollection() != system->getName()) ||
|
||||
CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() ==
|
||||
system->getName())) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "ADD/REMOVE GAMES TO THIS GAME COLLECTION", Font::get(FONT_SIZE_MEDIUM),
|
||||
0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if(UIModeController::getInstance()->isUIModeFull() && CollectionSystemManager::get()->isEditing())
|
||||
{
|
||||
if (UIModeController::getInstance()->isUIModeFull() &&
|
||||
CollectionSystemManager::get()->isEditing()) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "FINISH EDITING '" + Utils::String::toUpper(CollectionSystemManager::get()->getEditingCollection()) + "' COLLECTION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(
|
||||
mWindow, "FINISH EDITING '" + Utils::String::toUpper(
|
||||
CollectionSystemManager::get()->getEditingCollection()) +
|
||||
"' COLLECTION",Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder && !(mSystem->isCollection() && file->getType() == FOLDER))
|
||||
{
|
||||
if (UIModeController::getInstance()->isUIModeFull() && !fromPlaceholder &&
|
||||
!(mSystem->isCollection() && file->getType() == FOLDER)) {
|
||||
row.elements.clear();
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, "EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
||||
"EDIT THIS GAME'S METADATA", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(makeArrow(mWindow), false);
|
||||
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
|
||||
mMenu.addRow(row);
|
||||
}
|
||||
|
||||
// center the menu
|
||||
// Center the menu.
|
||||
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() - mMenu.getSize().y()) / 2);
|
||||
mMenu.setPosition((mSize.x() - mMenu.getSize().x()) / 2, (mSize.y() -
|
||||
mMenu.getSize().y()) / 2);
|
||||
}
|
||||
|
||||
GuiGamelistOptions::~GuiGamelistOptions()
|
||||
{
|
||||
// apply sort
|
||||
if (!fromPlaceholder) {
|
||||
FileData* root = mSystem->getRootFolder();
|
||||
root->sort(*mListSort->getSelected()); // will also recursively sort children
|
||||
|
||||
// notify that the root folder was sorted
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
// If a new sorting type was selected, then sort and update mSortTypeString for the system.
|
||||
if ((*mListSort->getSelected()).description != root->getSortTypeString()) {
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
// This will also recursively sort children.
|
||||
root->sort(*mListSort->getSelected(), mFavoritesSorting);
|
||||
root->setSortTypeString((*mListSort->getSelected()).description);
|
||||
|
||||
// Select the first row of the gamelist.
|
||||
FileData* firstRow = getGamelist()->getCursor()->getParent()->
|
||||
getChildrenListToDisplay()[0];
|
||||
getGamelist()->setCursor(firstRow);
|
||||
|
||||
// Notify that the root folder was sorted.
|
||||
getGamelist()->onFileChanged(root, FILE_SORTED);
|
||||
}
|
||||
}
|
||||
if (mFiltersChanged)
|
||||
{
|
||||
// only reload full view if we came from a placeholder
|
||||
// as we need to re-display the remaining elements for whatever new
|
||||
// game is selected
|
||||
if (mFiltersChanged) {
|
||||
// Only reload full view if we came from a placeholder as we need to
|
||||
// re-display the remaining elements for whatever new game is selected.
|
||||
ViewController::get()->reloadGameListView(mSystem);
|
||||
}
|
||||
}
|
||||
|
@ -181,20 +225,16 @@ void GuiGamelistOptions::openGamelistFilter()
|
|||
void GuiGamelistOptions::startEditMode()
|
||||
{
|
||||
std::string editingSystem = mSystem->getName();
|
||||
// need to check if we're editing the collections bundle, as we will want to edit the selected collection within
|
||||
if(editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName())
|
||||
{
|
||||
// Need to check if we're editing the collections bundle,
|
||||
// as we will want to edit the selected collection within.
|
||||
if (editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) {
|
||||
FileData* file = getGamelist()->getCursor();
|
||||
// do we have the cursor on a specific collection?
|
||||
// Do we have the cursor on a specific collection?.
|
||||
if (file->getType() == FOLDER)
|
||||
{
|
||||
editingSystem = file->getName();
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are inside a specific collection. We want to edit that one.
|
||||
// We are inside a specific collection. We want to edit that one.
|
||||
editingSystem = file->getSystem()->getName();
|
||||
}
|
||||
}
|
||||
CollectionSystemManager::get()->setEditMode(editingSystem);
|
||||
delete this;
|
||||
|
@ -208,8 +248,8 @@ void GuiGamelistOptions::exitEditMode()
|
|||
|
||||
void GuiGamelistOptions::openMetaDataEd()
|
||||
{
|
||||
// open metadata editor
|
||||
// get the FileData that hosts the original metadata
|
||||
// Open metadata editor.
|
||||
// Get the FileData that holds the original metadata.
|
||||
FileData* file = getGamelist()->getCursor()->getSourceFileData();
|
||||
ScraperSearchParams p;
|
||||
p.game = file;
|
||||
|
@ -217,106 +257,63 @@ void GuiGamelistOptions::openMetaDataEd()
|
|||
|
||||
std::function<void()> deleteBtnFunc;
|
||||
|
||||
if (file->getType() == FOLDER)
|
||||
{
|
||||
if (file->getType() == FOLDER) {
|
||||
deleteBtnFunc = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
deleteBtnFunc = [this, file] {
|
||||
CollectionSystemManager::get()->deleteCollectionFiles(file);
|
||||
ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true);
|
||||
};
|
||||
}
|
||||
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p, Utils::FileSystem::getFileName(file->getPath()),
|
||||
std::bind(&IGameListView::onFileChanged, ViewController::get()->getGameListView(file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
||||
mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p,
|
||||
Utils::FileSystem::getFileName(file->getPath()), std::bind(
|
||||
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
|
||||
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::jumpToLetter()
|
||||
{
|
||||
char letter = mJumpToLetterList->getSelected()[0];
|
||||
IGameListView* gamelist = getGamelist();
|
||||
|
||||
// this is a really shitty way to get a list of files
|
||||
const std::vector<FileData*>& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
// Get first row of the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
|
||||
long min = 0;
|
||||
long max = (long)files.size() - 1;
|
||||
long mid = 0;
|
||||
|
||||
while(max >= min)
|
||||
{
|
||||
mid = ((max - min) / 2) + min;
|
||||
|
||||
// game somehow has no first character to check
|
||||
if(files.at(mid)->getName().empty())
|
||||
continue;
|
||||
|
||||
char checkLetter = (char)toupper(files.at(mid)->getSortName()[0]);
|
||||
|
||||
if(checkLetter < letter)
|
||||
min = mid + 1;
|
||||
else if(checkLetter > letter || (mid > 0 && (letter == toupper(files.at(mid - 1)->getSortName()[0]))))
|
||||
max = mid - 1;
|
||||
else
|
||||
{
|
||||
// exact match found
|
||||
// step through games to exclude favorites
|
||||
if (firstFavorite != -1)
|
||||
{
|
||||
while(files[mid]->getFavorite())
|
||||
mid++;
|
||||
for (unsigned int i = 0; i < files.size(); i++) {
|
||||
if (mFavoritesSorting && mSystem->getName() != "favorites") {
|
||||
if ((char)toupper(files.at(i)->getSortName()[0]) ==
|
||||
letter && !files.at(i)->getFavorite()) {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if ((char)toupper(files.at(i)->getSortName()[0]) == letter) {
|
||||
getGamelist()->setCursor(files.at(i));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gamelist->setCursor(files.at(mid));
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::findFirstFavorite()
|
||||
void GuiGamelistOptions::jumpToFirstRow()
|
||||
{
|
||||
IGameListView* gamelist = getGamelist();
|
||||
|
||||
// this is a really shitty way to get a list of files
|
||||
const std::vector<FileData*>& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
|
||||
long loop = 0;
|
||||
long max = (long)files.size() - 1;
|
||||
|
||||
// Loop through the game list looking for the first game marked as a favorite
|
||||
while (!files[loop]->getFavorite() and loop < max)
|
||||
loop++;
|
||||
|
||||
// if the last entry in the game list was not a favorite then there were none for this system
|
||||
if (!files[loop]->getFavorite())
|
||||
{
|
||||
firstFavorite = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
firstFavorite = loop;
|
||||
}
|
||||
|
||||
void GuiGamelistOptions::jumpToFirstFavorite()
|
||||
{
|
||||
IGameListView* gamelist = getGamelist();
|
||||
|
||||
// this is a really shitty way to get a list of files
|
||||
const std::vector<FileData*>& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay();
|
||||
|
||||
gamelist->setCursor(files.at(firstFavorite));
|
||||
// Get first row of the gamelist.
|
||||
const std::vector<FileData*>& files = getGamelist()->getCursor()->
|
||||
getParent()->getChildrenListToDisplay();
|
||||
getGamelist()->setCursor(files.at(0));
|
||||
|
||||
delete this;
|
||||
}
|
||||
|
||||
bool GuiGamelistOptions::input(InputConfig* config, Input input)
|
||||
{
|
||||
if((config->isMappedTo("b", input) || config->isMappedTo("select", input)) && input.value)
|
||||
{
|
||||
if ((config->isMappedTo("b", input) ||
|
||||
config->isMappedTo("select", input)) && input.value) {
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
//
|
||||
// GuiGamelistOptions.h
|
||||
//
|
||||
// Gamelist options menu for the 'Jump to...' quick selector,
|
||||
// game sorting, game filters, and metadata edit.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
|
||||
#define ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
|
||||
|
@ -26,12 +33,11 @@ private:
|
|||
void openMetaDataEd();
|
||||
void startEditMode();
|
||||
void exitEditMode();
|
||||
|
||||
void jumpToLetter();
|
||||
void findFirstFavorite();
|
||||
void jumpToFirstFavorite();
|
||||
void jumpToFirstRow();
|
||||
|
||||
const std::string FAVORITE_CHAR = "\uF005";
|
||||
long firstFavorite = -1;
|
||||
|
||||
MenuComponent mMenu;
|
||||
|
||||
|
@ -43,6 +49,7 @@ private:
|
|||
|
||||
SystemData* mSystem;
|
||||
IGameListView* getGamelist();
|
||||
bool mFavoritesSorting;
|
||||
bool fromPlaceholder;
|
||||
bool mFiltersChanged;
|
||||
};
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GuiMenu.cpp
|
||||
//
|
||||
// Main menu.
|
||||
//
|
||||
|
||||
#include "guis/GuiMenu.h"
|
||||
|
||||
#include "components/OptionListComponent.h"
|
||||
|
@ -11,6 +17,7 @@
|
|||
#include "guis/GuiSettings.h"
|
||||
#include "views/UIModeController.h"
|
||||
#include "views/ViewController.h"
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
#include "CollectionSystemManager.h"
|
||||
#include "EmulationStation.h"
|
||||
#include "Scripting.h"
|
||||
|
@ -20,7 +27,11 @@
|
|||
#include <algorithm>
|
||||
#include "platform.h"
|
||||
|
||||
GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MENU"), mVersion(window)
|
||||
GuiMenu::GuiMenu(
|
||||
Window* window)
|
||||
: GuiComponent(window),
|
||||
mMenu(window, "MAIN MENU"),
|
||||
mVersion(window)
|
||||
{
|
||||
bool isFullUI = UIModeController::getInstance()->isUIModeFull();
|
||||
|
||||
|
@ -29,12 +40,12 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
|
|||
|
||||
addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundSettings(); });
|
||||
|
||||
|
||||
if (isFullUI)
|
||||
addEntry("UI SETTINGS", 0x777777FF, true, [this] { openUISettings(); });
|
||||
|
||||
if (isFullUI)
|
||||
addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openCollectionSystemSettings(); });
|
||||
addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] {
|
||||
openCollectionSystemSettings(); });
|
||||
|
||||
if (isFullUI)
|
||||
addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherSettings(); });
|
||||
|
@ -47,38 +58,44 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
|
|||
addChild(&mMenu);
|
||||
addVersionInfo();
|
||||
setSize(mMenu.getSize());
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, Renderer::getScreenHeight() * 0.15f);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
||||
Renderer::getScreenHeight() * 0.15f);
|
||||
}
|
||||
|
||||
void GuiMenu::openScraperSettings()
|
||||
{
|
||||
auto s = new GuiSettings(mWindow, "SCRAPER");
|
||||
|
||||
// scrape from
|
||||
auto scraper_list = std::make_shared< OptionListComponent< std::string > >(mWindow, "SCRAPE FROM", false);
|
||||
// Scrape from.
|
||||
auto scraper_list = std::make_shared< OptionListComponent< std::string >
|
||||
>(mWindow, "SCRAPE FROM", false);
|
||||
std::vector<std::string> scrapers = getScraperList();
|
||||
|
||||
// Select either the first entry of the one read from the settings, just in case the scraper from settings has vanished.
|
||||
for(auto it = scrapers.cbegin(); it != scrapers.cend(); it++)
|
||||
// Select either the first entry of the one read from the settings,
|
||||
// just in case the scraper from settings has vanished.
|
||||
for (auto it = scrapers.cbegin(); it != scrapers.cend(); it++)
|
||||
scraper_list->add(*it, *it, *it == Settings::getInstance()->getString("Scraper"));
|
||||
|
||||
s->addWithLabel("SCRAPE FROM", scraper_list);
|
||||
s->addSaveFunc([scraper_list] { Settings::getInstance()->setString("Scraper", scraper_list->getSelected()); });
|
||||
s->addSaveFunc([scraper_list] { Settings::getInstance()->setString("Scraper",
|
||||
scraper_list->getSelected()); });
|
||||
|
||||
// scrape ratings
|
||||
// Scrape ratings.
|
||||
auto scrape_ratings = std::make_shared<SwitchComponent>(mWindow);
|
||||
scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings"));
|
||||
s->addWithLabel("SCRAPE RATINGS", scrape_ratings);
|
||||
s->addSaveFunc([scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings", scrape_ratings->getState()); });
|
||||
s->addSaveFunc([scrape_ratings] { Settings::getInstance()->setBool("ScrapeRatings",
|
||||
scrape_ratings->getState()); });
|
||||
|
||||
// scrape now
|
||||
// Scrape now.
|
||||
ComponentListRow row;
|
||||
auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); };
|
||||
std::function<void()> openAndSave = openScrapeNow;
|
||||
openAndSave = [s, openAndSave] { s->save(); openAndSave(); };
|
||||
row.makeAcceptInputHandler(openAndSave);
|
||||
|
||||
auto scrape_now = std::make_shared<TextComponent>(mWindow, "SCRAPE NOW", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto scrape_now = std::make_shared<TextComponent>
|
||||
(mWindow, "SCRAPE NOW", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||
auto bracket = makeArrow(mWindow);
|
||||
row.addElement(scrape_now, true);
|
||||
row.addElement(bracket, false);
|
||||
|
@ -91,24 +108,25 @@ void GuiMenu::openSoundSettings()
|
|||
{
|
||||
auto s = new GuiSettings(mWindow, "SOUND SETTINGS");
|
||||
|
||||
// volume
|
||||
// System volume.
|
||||
auto volume = std::make_shared<SliderComponent>(mWindow, 0.f, 100.f, 1.f, "%");
|
||||
volume->setValue((float)VolumeControl::getInstance()->getVolume());
|
||||
s->addWithLabel("SYSTEM VOLUME", volume);
|
||||
s->addSaveFunc([volume] { VolumeControl::getInstance()->setVolume((int)Math::round(volume->getValue())); });
|
||||
s->addSaveFunc([volume] { VolumeControl::getInstance()->
|
||||
setVolume((int)Math::round(volume->getValue())); });
|
||||
|
||||
if (UIModeController::getInstance()->isUIModeFull())
|
||||
{
|
||||
if (UIModeController::getInstance()->isUIModeFull()) {
|
||||
#if defined(__linux__)
|
||||
// audio card
|
||||
auto audio_card = std::make_shared< OptionListComponent<std::string> >(mWindow, "AUDIO CARD", false);
|
||||
auto audio_card = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "AUDIO CARD", false);
|
||||
std::vector<std::string> audio_cards;
|
||||
#ifdef _RPI_
|
||||
#ifdef _RPI_
|
||||
// RPi Specific Audio Cards
|
||||
audio_cards.push_back("local");
|
||||
audio_cards.push_back("hdmi");
|
||||
audio_cards.push_back("both");
|
||||
#endif
|
||||
#endif
|
||||
audio_cards.push_back("default");
|
||||
audio_cards.push_back("sysdefault");
|
||||
audio_cards.push_back("dmix");
|
||||
|
@ -116,11 +134,12 @@ void GuiMenu::openSoundSettings()
|
|||
audio_cards.push_back("plughw");
|
||||
audio_cards.push_back("null");
|
||||
if (Settings::getInstance()->getString("AudioCard") != "") {
|
||||
if(std::find(audio_cards.begin(), audio_cards.end(), Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) {
|
||||
if (std::find(audio_cards.begin(), audio_cards.end(),
|
||||
Settings::getInstance()->getString("AudioCard")) == audio_cards.end()) {
|
||||
audio_cards.push_back(Settings::getInstance()->getString("AudioCard"));
|
||||
}
|
||||
}
|
||||
for(auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++)
|
||||
for (auto ac = audio_cards.cbegin(); ac != audio_cards.cend(); ac++)
|
||||
audio_card->add(*ac, *ac, Settings::getInstance()->getString("AudioCard") == *ac);
|
||||
s->addWithLabel("AUDIO CARD", audio_card);
|
||||
s->addSaveFunc([audio_card] {
|
||||
|
@ -129,8 +148,9 @@ void GuiMenu::openSoundSettings()
|
|||
VolumeControl::getInstance()->init();
|
||||
});
|
||||
|
||||
// volume control device
|
||||
auto vol_dev = std::make_shared< OptionListComponent<std::string> >(mWindow, "AUDIO DEVICE", false);
|
||||
// Volume control device.
|
||||
auto vol_dev = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "AUDIO DEVICE", false);
|
||||
std::vector<std::string> transitions;
|
||||
transitions.push_back("PCM");
|
||||
transitions.push_back("Speaker");
|
||||
|
@ -138,11 +158,12 @@ void GuiMenu::openSoundSettings()
|
|||
transitions.push_back("Digital");
|
||||
transitions.push_back("Analogue");
|
||||
if (Settings::getInstance()->getString("AudioDevice") != "") {
|
||||
if(std::find(transitions.begin(), transitions.end(), Settings::getInstance()->getString("AudioDevice")) == transitions.end()) {
|
||||
if (std::find(transitions.begin(), transitions.end(),
|
||||
Settings::getInstance()->getString("AudioDevice")) == transitions.end()) {
|
||||
transitions.push_back(Settings::getInstance()->getString("AudioDevice"));
|
||||
}
|
||||
}
|
||||
for(auto it = transitions.cbegin(); it != transitions.cend(); it++)
|
||||
for (auto it = transitions.cbegin(); it != transitions.cend(); it++)
|
||||
vol_dev->add(*it, *it, Settings::getInstance()->getString("AudioDevice") == *it);
|
||||
s->addWithLabel("AUDIO DEVICE", vol_dev);
|
||||
s->addSaveFunc([vol_dev] {
|
||||
|
@ -152,7 +173,7 @@ void GuiMenu::openSoundSettings()
|
|||
});
|
||||
#endif
|
||||
|
||||
// disable sounds
|
||||
// Disable sounds.
|
||||
auto sounds_enabled = std::make_shared<SwitchComponent>(mWindow);
|
||||
sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds"));
|
||||
s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled);
|
||||
|
@ -170,11 +191,13 @@ void GuiMenu::openSoundSettings()
|
|||
auto video_audio = std::make_shared<SwitchComponent>(mWindow);
|
||||
video_audio->setState(Settings::getInstance()->getBool("VideoAudio"));
|
||||
s->addWithLabel("ENABLE VIDEO AUDIO", video_audio);
|
||||
s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio", video_audio->getState()); });
|
||||
s->addSaveFunc([video_audio] { Settings::getInstance()->setBool("VideoAudio",
|
||||
video_audio->getState()); });
|
||||
|
||||
#ifdef _RPI_
|
||||
// OMX player Audio Device
|
||||
auto omx_audio_dev = std::make_shared< OptionListComponent<std::string> >(mWindow, "OMX PLAYER AUDIO DEVICE", false);
|
||||
auto omx_audio_dev = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "OMX PLAYER AUDIO DEVICE", false);
|
||||
std::vector<std::string> omx_cards;
|
||||
// RPi Specific Audio Cards
|
||||
omx_cards.push_back("local");
|
||||
|
@ -183,7 +206,8 @@ void GuiMenu::openSoundSettings()
|
|||
omx_cards.push_back("alsa:hw:0,0");
|
||||
omx_cards.push_back("alsa:hw:1,0");
|
||||
if (Settings::getInstance()->getString("OMXAudioDev") != "") {
|
||||
if (std::find(omx_cards.begin(), omx_cards.end(), Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) {
|
||||
if (std::find(omx_cards.begin(), omx_cards.end(),
|
||||
Settings::getInstance()->getString("OMXAudioDev")) == omx_cards.end()) {
|
||||
omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev"));
|
||||
}
|
||||
}
|
||||
|
@ -198,26 +222,25 @@ void GuiMenu::openSoundSettings()
|
|||
}
|
||||
|
||||
mWindow->pushGui(s);
|
||||
|
||||
}
|
||||
|
||||
void GuiMenu::openUISettings()
|
||||
{
|
||||
auto s = new GuiSettings(mWindow, "UI SETTINGS");
|
||||
|
||||
//UI mode
|
||||
auto UImodeSelection = std::make_shared< OptionListComponent<std::string> >(mWindow, "UI MODE", false);
|
||||
// UI mode.
|
||||
auto UImodeSelection = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "UI MODE", false);
|
||||
std::vector<std::string> UImodes = UIModeController::getInstance()->getUIModes();
|
||||
for (auto it = UImodes.cbegin(); it != UImodes.cend(); it++)
|
||||
UImodeSelection->add(*it, *it, Settings::getInstance()->getString("UIMode") == *it);
|
||||
s->addWithLabel("UI MODE", UImodeSelection);
|
||||
Window* window = mWindow;
|
||||
s->addSaveFunc([ UImodeSelection, window]
|
||||
{
|
||||
s->addSaveFunc([ UImodeSelection, window] {
|
||||
std::string selectedMode = UImodeSelection->getSelected();
|
||||
if (selectedMode != "Full")
|
||||
{
|
||||
std::string msg = "You are changing the UI to a restricted mode:\n" + selectedMode + "\n";
|
||||
if (selectedMode != "Full") {
|
||||
std::string msg = "You are changing the UI to a restricted mode:\n" +
|
||||
selectedMode + "\n";
|
||||
msg += "This will hide most menu-options to prevent changes to the system.\n";
|
||||
msg += "To unlock and return to the full UI, enter this code: \n";
|
||||
msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n";
|
||||
|
@ -231,109 +254,114 @@ void GuiMenu::openUISettings()
|
|||
}
|
||||
});
|
||||
|
||||
// fullscreen mode
|
||||
auto fullscreen_mode = std::make_shared< OptionListComponent<std::string> >(mWindow, "FULLSCREEN MODE", false);
|
||||
// Fullscreen mode.
|
||||
auto fullscreen_mode = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "FULLSCREEN MODE", false);
|
||||
std::vector<std::string> screenmode;
|
||||
screenmode.push_back("normal");
|
||||
screenmode.push_back("borderless");
|
||||
for(auto it = screenmode.cbegin(); it != screenmode.cend(); it++)
|
||||
for (auto it = screenmode.cbegin(); it != screenmode.cend(); it++)
|
||||
fullscreen_mode->add(*it, *it, Settings::getInstance()->getString("FullscreenMode") == *it);
|
||||
s->addWithLabel("FULLSCREEN MODE (REQUIRES RESTART)", fullscreen_mode);
|
||||
s->addSaveFunc([fullscreen_mode] {
|
||||
if (Settings::getInstance()->getString("FullscreenMode") == "normal"
|
||||
&& fullscreen_mode->getSelected() != "normal")
|
||||
{
|
||||
&& fullscreen_mode->getSelected() != "normal") {
|
||||
Settings::getInstance()->setString("PowerSaverMode", "default");
|
||||
PowerSaver::init();
|
||||
}
|
||||
Settings::getInstance()->setString("FullscreenMode", fullscreen_mode->getSelected());
|
||||
});
|
||||
|
||||
// screensaver
|
||||
// Screensaver.
|
||||
ComponentListRow screensaver_row;
|
||||
screensaver_row.elements.clear();
|
||||
screensaver_row.addElement(std::make_shared<TextComponent>(mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
screensaver_row.addElement(std::make_shared<TextComponent>
|
||||
(mWindow, "SCREENSAVER SETTINGS", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
screensaver_row.addElement(makeArrow(mWindow), false);
|
||||
screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this));
|
||||
s->addRow(screensaver_row);
|
||||
|
||||
// quick system select (left/right in game list view)
|
||||
// Quick system select (left/right in game list view).
|
||||
auto quick_sys_select = std::make_shared<SwitchComponent>(mWindow);
|
||||
quick_sys_select->setState(Settings::getInstance()->getBool("QuickSystemSelect"));
|
||||
s->addWithLabel("QUICK SYSTEM SELECT", quick_sys_select);
|
||||
s->addSaveFunc([quick_sys_select] { Settings::getInstance()->setBool("QuickSystemSelect", quick_sys_select->getState()); });
|
||||
s->addSaveFunc([quick_sys_select] { Settings::getInstance()->setBool("QuickSystemSelect",
|
||||
quick_sys_select->getState()); });
|
||||
|
||||
// carousel transition option
|
||||
// Carousel transition option.
|
||||
auto move_carousel = std::make_shared<SwitchComponent>(mWindow);
|
||||
move_carousel->setState(Settings::getInstance()->getBool("MoveCarousel"));
|
||||
s->addWithLabel("CAROUSEL TRANSITIONS", move_carousel);
|
||||
s->addSaveFunc([move_carousel] {
|
||||
if (move_carousel->getState()
|
||||
&& !Settings::getInstance()->getBool("MoveCarousel")
|
||||
&& PowerSaver::getMode() == PowerSaver::INSTANT)
|
||||
{
|
||||
&& PowerSaver::getMode() == PowerSaver::INSTANT) {
|
||||
Settings::getInstance()->setString("PowerSaverMode", "default");
|
||||
PowerSaver::init();
|
||||
}
|
||||
Settings::getInstance()->setBool("MoveCarousel", move_carousel->getState());
|
||||
});
|
||||
|
||||
// transition style
|
||||
auto transition_style = std::make_shared< OptionListComponent<std::string> >(mWindow, "TRANSITION STYLE", false);
|
||||
// Transition style.
|
||||
auto transition_style = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "TRANSITION STYLE", false);
|
||||
std::vector<std::string> transitions;
|
||||
transitions.push_back("fade");
|
||||
transitions.push_back("slide");
|
||||
transitions.push_back("instant");
|
||||
for(auto it = transitions.cbegin(); it != transitions.cend(); it++)
|
||||
transition_style->add(*it, *it, Settings::getInstance()->getString("TransitionStyle") == *it);
|
||||
for (auto it = transitions.cbegin(); it != transitions.cend(); it++)
|
||||
transition_style->add(*it, *it, Settings::getInstance()->
|
||||
getString("TransitionStyle") == *it);
|
||||
s->addWithLabel("TRANSITION STYLE", transition_style);
|
||||
s->addSaveFunc([transition_style] {
|
||||
if (Settings::getInstance()->getString("TransitionStyle") == "instant"
|
||||
&& transition_style->getSelected() != "instant"
|
||||
&& PowerSaver::getMode() == PowerSaver::INSTANT)
|
||||
{
|
||||
&& PowerSaver::getMode() == PowerSaver::INSTANT) {
|
||||
Settings::getInstance()->setString("PowerSaverMode", "default");
|
||||
PowerSaver::init();
|
||||
}
|
||||
Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected());
|
||||
});
|
||||
|
||||
// theme set
|
||||
// Theme selection.
|
||||
auto themeSets = ThemeData::getThemeSets();
|
||||
|
||||
if(!themeSets.empty())
|
||||
if (!themeSets.empty())
|
||||
{
|
||||
std::map<std::string, ThemeSet>::const_iterator selectedSet = themeSets.find(Settings::getInstance()->getString("ThemeSet"));
|
||||
if(selectedSet == themeSets.cend())
|
||||
std::map<std::string, ThemeSet>::const_iterator selectedSet =
|
||||
themeSets.find(Settings::getInstance()->getString("ThemeSet"));
|
||||
if (selectedSet == themeSets.cend())
|
||||
selectedSet = themeSets.cbegin();
|
||||
|
||||
auto theme_set = std::make_shared< OptionListComponent<std::string> >(mWindow, "THEME SET", false);
|
||||
for(auto it = themeSets.cbegin(); it != themeSets.cend(); it++)
|
||||
auto theme_set = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "THEME SET", false);
|
||||
for (auto it = themeSets.cbegin(); it != themeSets.cend(); it++)
|
||||
theme_set->add(it->first, it->first, it == selectedSet);
|
||||
s->addWithLabel("THEME SET", theme_set);
|
||||
|
||||
Window* window = mWindow;
|
||||
s->addSaveFunc([window, theme_set]
|
||||
{
|
||||
s->addSaveFunc([window, theme_set] {
|
||||
bool needReload = false;
|
||||
std::string oldTheme = Settings::getInstance()->getString("ThemeSet");
|
||||
if(oldTheme != theme_set->getSelected())
|
||||
if (oldTheme != theme_set->getSelected())
|
||||
needReload = true;
|
||||
|
||||
Settings::getInstance()->setString("ThemeSet", theme_set->getSelected());
|
||||
|
||||
if(needReload)
|
||||
if (needReload)
|
||||
{
|
||||
Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme);
|
||||
CollectionSystemManager::get()->updateSystemsList();
|
||||
ViewController::get()->goToStart();
|
||||
ViewController::get()->reloadAll(); // TODO - replace this with some sort of signal-based implementation
|
||||
// TODO - replace this with some sort of signal-based implementation.
|
||||
ViewController::get()->reloadAll();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// GameList view style
|
||||
auto gamelist_style = std::make_shared< OptionListComponent<std::string> >(mWindow, "GAMELIST VIEW STYLE", false);
|
||||
// GameList view style.
|
||||
auto gamelist_style = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "GAMELIST VIEW STYLE", false);
|
||||
std::vector<std::string> styles;
|
||||
styles.push_back("automatic");
|
||||
styles.push_back("basic");
|
||||
|
@ -342,92 +370,121 @@ void GuiMenu::openUISettings()
|
|||
styles.push_back("grid");
|
||||
|
||||
for (auto it = styles.cbegin(); it != styles.cend(); it++)
|
||||
gamelist_style->add(*it, *it, Settings::getInstance()->getString("GamelistViewStyle") == *it);
|
||||
gamelist_style->add(*it, *it, Settings::getInstance()->
|
||||
getString("GamelistViewStyle") == *it);
|
||||
s->addWithLabel("GAMELIST VIEW STYLE", gamelist_style);
|
||||
s->addSaveFunc([gamelist_style] {
|
||||
bool needReload = false;
|
||||
if (Settings::getInstance()->getString("GamelistViewStyle") != gamelist_style->getSelected())
|
||||
if (Settings::getInstance()->getString("GamelistViewStyle") !=
|
||||
gamelist_style->getSelected())
|
||||
needReload = true;
|
||||
Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected());
|
||||
if (needReload)
|
||||
ViewController::get()->reloadAll();
|
||||
});
|
||||
|
||||
// Optionally start in selected system
|
||||
auto systemfocus_list = std::make_shared< OptionListComponent<std::string> >(mWindow, "START ON SYSTEM", false);
|
||||
// Optionally start in selected system.
|
||||
auto systemfocus_list = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "START ON SYSTEM", false);
|
||||
systemfocus_list->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == "");
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++)
|
||||
{
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
if ("retropie" != (*it)->getName())
|
||||
{
|
||||
systemfocus_list->add((*it)->getName(), (*it)->getName(), Settings::getInstance()->getString("StartupSystem") == (*it)->getName());
|
||||
}
|
||||
systemfocus_list->add((*it)->getName(), (*it)->getName(),
|
||||
Settings::getInstance()->getString("StartupSystem") == (*it)->getName());
|
||||
}
|
||||
s->addWithLabel("START ON SYSTEM", systemfocus_list);
|
||||
s->addSaveFunc([systemfocus_list] {
|
||||
Settings::getInstance()->setString("StartupSystem", systemfocus_list->getSelected());
|
||||
});
|
||||
|
||||
// show help
|
||||
// Show help.
|
||||
auto show_help = std::make_shared<SwitchComponent>(mWindow);
|
||||
show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts"));
|
||||
s->addWithLabel("ON-SCREEN HELP", show_help);
|
||||
s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts", show_help->getState()); });
|
||||
s->addSaveFunc([show_help] { Settings::getInstance()->setBool("ShowHelpPrompts",
|
||||
show_help->getState()); });
|
||||
|
||||
// enable filters (ForceDisableFilters)
|
||||
// Enable filters (ForceDisableFilters).
|
||||
auto enable_filter = std::make_shared<SwitchComponent>(mWindow);
|
||||
enable_filter->setState(!Settings::getInstance()->getBool("ForceDisableFilters"));
|
||||
s->addWithLabel("ENABLE FILTERS", enable_filter);
|
||||
s->addWithLabel("ENABLE GAMELIST FILTERS", enable_filter);
|
||||
s->addSaveFunc([enable_filter] {
|
||||
bool filter_is_enabled = !Settings::getInstance()->getBool("ForceDisableFilters");
|
||||
Settings::getInstance()->setBool("ForceDisableFilters", !enable_filter->getState());
|
||||
if (enable_filter->getState() != filter_is_enabled) ViewController::get()->ReloadAndGoToStart();
|
||||
if (enable_filter->getState() != filter_is_enabled)
|
||||
ViewController::get()->ReloadAndGoToStart();
|
||||
});
|
||||
|
||||
// hide start menu in Kid Mode
|
||||
// Hide start menu in Kid Mode.
|
||||
auto disable_start = std::make_shared<SwitchComponent>(mWindow);
|
||||
disable_start->setState(Settings::getInstance()->getBool("DisableKidStartMenu"));
|
||||
s->addWithLabel("DISABLE START MENU IN KID MODE", disable_start);
|
||||
s->addSaveFunc([disable_start] { Settings::getInstance()->setBool("DisableKidStartMenu", disable_start->getState()); });
|
||||
s->addSaveFunc([disable_start] { Settings::getInstance()->setBool("DisableKidStartMenu",
|
||||
disable_start->getState()); });
|
||||
|
||||
// hide Reboot System option in the Quit menu
|
||||
// Hide Reboot System option in the quit menu.
|
||||
auto show_rebootsystem = std::make_shared<SwitchComponent>(mWindow);
|
||||
show_rebootsystem->setState(Settings::getInstance()->getBool("ShowRebootSystem"));
|
||||
s->addWithLabel("SHOW \"REBOOT SYSTEM\" MENU ENTRY", show_rebootsystem);
|
||||
s->addSaveFunc([show_rebootsystem] { Settings::getInstance()->setBool("ShowRebootSystem", show_rebootsystem->getState()); });
|
||||
s->addSaveFunc([show_rebootsystem] { Settings::getInstance()->setBool("ShowRebootSystem",
|
||||
show_rebootsystem->getState()); });
|
||||
|
||||
// hide Power Off System option in the Quit menu
|
||||
// Hide Power Off System option in the quit menu.
|
||||
auto show_poweroffsystem = std::make_shared<SwitchComponent>(mWindow);
|
||||
show_poweroffsystem->setState(Settings::getInstance()->getBool("ShowPoweroffSystem"));
|
||||
s->addWithLabel("SHOW \"POWER OFF SYSTEM\" MENU ENTRY", show_poweroffsystem);
|
||||
s->addSaveFunc([show_poweroffsystem] { Settings::getInstance()->setBool("ShowPoweroffSystem", show_poweroffsystem->getState()); });
|
||||
s->addSaveFunc([show_poweroffsystem] { Settings::getInstance()->setBool("ShowPoweroffSystem",
|
||||
show_poweroffsystem->getState()); });
|
||||
|
||||
// Show favorites first in gamelists
|
||||
// Sort favorites on top of the gamelists.
|
||||
auto favoritesFirstSwitch = std::make_shared<SwitchComponent>(mWindow);
|
||||
favoritesFirstSwitch->setState(Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
s->addWithLabel("SHOW FAVORITES ON TOP OF GAMELIST", favoritesFirstSwitch);
|
||||
s->addSaveFunc([favoritesFirstSwitch]
|
||||
{
|
||||
s->addWithLabel("SORT FAVORITES ON TOP OF GAMELISTS", favoritesFirstSwitch);
|
||||
s->addSaveFunc([favoritesFirstSwitch] {
|
||||
if (Settings::getInstance()->setBool("FavoritesFirst", favoritesFirstSwitch->getState()))
|
||||
ViewController::get()->reloadAll();
|
||||
for (auto it = SystemData::sSystemVector.cbegin(); it !=
|
||||
SystemData::sSystemVector.cend(); it++) {
|
||||
|
||||
// The favorites and recent gamelists never sort favorites on top.
|
||||
if ((*it)->getName() == "favorites" || (*it)->getName() == "recent")
|
||||
continue;
|
||||
|
||||
// Don't re-sort custom collections as they have their own option
|
||||
// for whether to sort favorites on top or not (FavFirstCustom).
|
||||
if (CollectionSystemManager::get()->getIsCustomCollection((*it)))
|
||||
continue;
|
||||
|
||||
FileData* rootFolder = (*it)->getRootFolder();
|
||||
|
||||
rootFolder->sort(getSortTypeFromString(rootFolder->getSortTypeString()),
|
||||
Settings::getInstance()->getBool("FavoritesFirst"));
|
||||
ViewController::get()->reloadGameListView(*it);
|
||||
|
||||
// Jump to the first row of the game list.
|
||||
IGameListView* gameList = ViewController::get()->getGameListView((*it)).get();
|
||||
FileData* firstRow = gameList->getCursor()->getParent()->getChildrenListToDisplay()[0];
|
||||
gameList->setCursor(firstRow);
|
||||
}
|
||||
});
|
||||
|
||||
mWindow->pushGui(s);
|
||||
|
||||
}
|
||||
|
||||
void GuiMenu::openOtherSettings()
|
||||
{
|
||||
auto s = new GuiSettings(mWindow, "OTHER SETTINGS");
|
||||
|
||||
// maximum vram
|
||||
// Maximum VRAM.
|
||||
auto max_vram = std::make_shared<SliderComponent>(mWindow, 0.f, 1000.f, 10.f, "Mb");
|
||||
max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM")));
|
||||
s->addWithLabel("VRAM LIMIT", max_vram);
|
||||
s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM", (int)Math::round(max_vram->getValue())); });
|
||||
s->addSaveFunc([max_vram] { Settings::getInstance()->setInt("MaxVRAM",
|
||||
(int)Math::round(max_vram->getValue())); });
|
||||
|
||||
// power saver
|
||||
auto power_saver = std::make_shared< OptionListComponent<std::string> >(mWindow, "POWER SAVER MODES", false);
|
||||
// Power saver.
|
||||
auto power_saver = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "POWER SAVER MODES", false);
|
||||
std::vector<std::string> modes;
|
||||
modes.push_back("disabled");
|
||||
modes.push_back("default");
|
||||
|
@ -437,7 +494,8 @@ void GuiMenu::openOtherSettings()
|
|||
power_saver->add(*it, *it, Settings::getInstance()->getString("PowerSaverMode") == *it);
|
||||
s->addWithLabel("POWER SAVER MODES", power_saver);
|
||||
s->addSaveFunc([this, power_saver] {
|
||||
if (Settings::getInstance()->getString("PowerSaverMode") != "instant" && power_saver->getSelected() == "instant") {
|
||||
if (Settings::getInstance()->getString("PowerSaverMode") !=
|
||||
"instant" && power_saver->getSelected() == "instant") {
|
||||
Settings::getInstance()->setString("TransitionStyle", "instant");
|
||||
Settings::getInstance()->setBool("MoveCarousel", false);
|
||||
Settings::getInstance()->setBool("EnableSounds", false);
|
||||
|
@ -446,15 +504,17 @@ void GuiMenu::openOtherSettings()
|
|||
PowerSaver::init();
|
||||
});
|
||||
|
||||
// gamelists
|
||||
auto gamelistsSaveMode = std::make_shared< OptionListComponent<std::string> >(mWindow, "SAVE METADATA", false);
|
||||
// Gamelists.
|
||||
auto gamelistsSaveMode = std::make_shared< OptionListComponent<std::string>
|
||||
>(mWindow, "SAVE METADATA", false);
|
||||
std::vector<std::string> saveModes;
|
||||
saveModes.push_back("on exit");
|
||||
saveModes.push_back("always");
|
||||
saveModes.push_back("never");
|
||||
|
||||
for(auto it = saveModes.cbegin(); it != saveModes.cend(); it++)
|
||||
gamelistsSaveMode->add(*it, *it, Settings::getInstance()->getString("SaveGamelistsMode") == *it);
|
||||
for (auto it = saveModes.cbegin(); it != saveModes.cend(); it++)
|
||||
gamelistsSaveMode->add(*it, *it, Settings::getInstance()->
|
||||
getString("SaveGamelistsMode") == *it);
|
||||
s->addWithLabel("SAVE METADATA", gamelistsSaveMode);
|
||||
s->addSaveFunc([gamelistsSaveMode] {
|
||||
Settings::getInstance()->setString("SaveGamelistsMode", gamelistsSaveMode->getSelected());
|
||||
|
@ -463,51 +523,56 @@ void GuiMenu::openOtherSettings()
|
|||
auto parse_gamelists = std::make_shared<SwitchComponent>(mWindow);
|
||||
parse_gamelists->setState(Settings::getInstance()->getBool("ParseGamelistOnly"));
|
||||
s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists);
|
||||
s->addSaveFunc([parse_gamelists] { Settings::getInstance()->setBool("ParseGamelistOnly", parse_gamelists->getState()); });
|
||||
s->addSaveFunc([parse_gamelists] { Settings::getInstance()->
|
||||
setBool("ParseGamelistOnly", parse_gamelists->getState()); });
|
||||
|
||||
auto local_art = std::make_shared<SwitchComponent>(mWindow);
|
||||
local_art->setState(Settings::getInstance()->getBool("LocalArt"));
|
||||
s->addWithLabel("SEARCH FOR LOCAL ART", local_art);
|
||||
s->addSaveFunc([local_art] { Settings::getInstance()->setBool("LocalArt", local_art->getState()); });
|
||||
s->addSaveFunc([local_art] { Settings::getInstance()->
|
||||
setBool("LocalArt", local_art->getState()); });
|
||||
|
||||
// Allow overriding of the launch string per game (the option to disable this is intended primarily for testing purposes)
|
||||
// Allow overriding of the launch string per game (the option
|
||||
// to disable this is intended primarily for testing purposes).
|
||||
auto launchstring_override = std::make_shared<SwitchComponent>(mWindow);
|
||||
launchstring_override->setState(Settings::getInstance()->getBool("LaunchstringOverride"));
|
||||
s->addWithLabel("PER GAME OVERRIDE OF LAUNCH STRING", launchstring_override);
|
||||
s->addSaveFunc([launchstring_override] { Settings::getInstance()->setBool("LaunchstringOverride", launchstring_override->getState()); });
|
||||
s->addWithLabel("PER GAME LAUNCH STRING OVERRIDE", launchstring_override);
|
||||
s->addSaveFunc([launchstring_override] { Settings::getInstance()->
|
||||
setBool("LaunchstringOverride", launchstring_override->getState()); });
|
||||
|
||||
// hidden files
|
||||
// Hidden files.
|
||||
auto hidden_files = std::make_shared<SwitchComponent>(mWindow);
|
||||
hidden_files->setState(Settings::getInstance()->getBool("ShowHiddenFiles"));
|
||||
s->addWithLabel("SHOW HIDDEN FILES", hidden_files);
|
||||
s->addSaveFunc([hidden_files] { Settings::getInstance()->setBool("ShowHiddenFiles", hidden_files->getState()); });
|
||||
s->addSaveFunc([hidden_files] { Settings::getInstance()->setBool("ShowHiddenFiles",
|
||||
hidden_files->getState()); });
|
||||
|
||||
#ifdef _RPI_
|
||||
// Video Player - VideoOmxPlayer
|
||||
// Video Player - VideoOmxPlayer.
|
||||
auto omx_player = std::make_shared<SwitchComponent>(mWindow);
|
||||
omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer"));
|
||||
s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player);
|
||||
s->addSaveFunc([omx_player]
|
||||
{
|
||||
// need to reload all views to re-create the right video components
|
||||
// Need to reload all views to re-create the right video components.
|
||||
bool needReload = false;
|
||||
if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState())
|
||||
if (Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState())
|
||||
needReload = true;
|
||||
|
||||
Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState());
|
||||
|
||||
if(needReload)
|
||||
if (needReload)
|
||||
ViewController::get()->reloadAll();
|
||||
});
|
||||
|
||||
#endif
|
||||
|
||||
// framerate
|
||||
// Framerate.
|
||||
auto framerate = std::make_shared<SwitchComponent>(mWindow);
|
||||
framerate->setState(Settings::getInstance()->getBool("DrawFramerate"));
|
||||
s->addWithLabel("SHOW FRAMERATE", framerate);
|
||||
s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate", framerate->getState()); });
|
||||
|
||||
s->addSaveFunc([framerate] { Settings::getInstance()->setBool("DrawFramerate",
|
||||
framerate->getState()); });
|
||||
|
||||
mWindow->pushGui(s);
|
||||
|
||||
|
@ -521,7 +586,6 @@ void GuiMenu::openConfigInput()
|
|||
window->pushGui(new GuiDetectDevice(window, false, nullptr));
|
||||
}, "NO", nullptr)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
void GuiMenu::openQuitMenu()
|
||||
|
@ -531,10 +595,8 @@ void GuiMenu::openQuitMenu()
|
|||
Window* window = mWindow;
|
||||
|
||||
ComponentListRow row;
|
||||
if (UIModeController::getInstance()->isUIModeFull())
|
||||
{
|
||||
if(Settings::getInstance()->getBool("ShowExit"))
|
||||
{
|
||||
if (UIModeController::getInstance()->isUIModeFull()) {
|
||||
if (Settings::getInstance()->getBool("ShowExit")) {
|
||||
row.makeAcceptInputHandler([window] {
|
||||
window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES",
|
||||
[] {
|
||||
|
@ -542,13 +604,13 @@ void GuiMenu::openQuitMenu()
|
|||
quitES();
|
||||
}, "NO", nullptr));
|
||||
});
|
||||
row.addElement(std::make_shared<TextComponent>(window, "QUIT EMULATIONSTATION", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(window, "QUIT EMULATIONSTATION",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
s->addRow(row);
|
||||
}
|
||||
}
|
||||
|
||||
if(Settings::getInstance()->getBool("ShowRebootSystem"))
|
||||
{
|
||||
if (Settings::getInstance()->getBool("ShowRebootSystem")) {
|
||||
row.elements.clear();
|
||||
row.makeAcceptInputHandler([window] {
|
||||
window->pushGui(new GuiMsgBox(window, "REALLY REBOOT?", "YES",
|
||||
|
@ -559,12 +621,12 @@ void GuiMenu::openQuitMenu()
|
|||
LOG(LogWarning) << "Reboot terminated with non-zero result!";
|
||||
}, "NO", nullptr));
|
||||
});
|
||||
row.addElement(std::make_shared<TextComponent>(window, "REBOOT SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(window, "REBOOT SYSTEM",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
s->addRow(row);
|
||||
}
|
||||
|
||||
if(Settings::getInstance()->getBool("ShowPoweroffSystem"))
|
||||
{
|
||||
if (Settings::getInstance()->getBool("ShowPoweroffSystem")) {
|
||||
row.elements.clear();
|
||||
row.makeAcceptInputHandler([window] {
|
||||
window->pushGui(new GuiMsgBox(window, "REALLY POWER OFF?", "YES",
|
||||
|
@ -575,7 +637,8 @@ void GuiMenu::openQuitMenu()
|
|||
LOG(LogWarning) << "Power off terminated with non-zero result!";
|
||||
}, "NO", nullptr));
|
||||
});
|
||||
row.addElement(std::make_shared<TextComponent>(window, "POWER OFF SYSTEM", Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
row.addElement(std::make_shared<TextComponent>(window, "POWER OFF SYSTEM",
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
||||
s->addRow(row);
|
||||
}
|
||||
|
||||
|
@ -584,8 +647,6 @@ void GuiMenu::openQuitMenu()
|
|||
|
||||
void GuiMenu::addVersionInfo()
|
||||
{
|
||||
// std::string buildDate = (Settings::getInstance()->getBool("Debug") ? std::string( " (" + Utils::String::toUpper(PROGRAM_BUILT_STRING) + ")") : (""));
|
||||
|
||||
mVersion.setFont(Font::get(FONT_SIZE_SMALL));
|
||||
mVersion.setColor(0x5E5E5EFF);
|
||||
mVersion.setText("EMULATIONSTATION-DE V" + Utils::String::toUpper(PROGRAM_VERSION_STRING));
|
||||
|
@ -607,16 +668,19 @@ void GuiMenu::onSizeChanged()
|
|||
mVersion.setPosition(0, mSize.y() - mVersion.getSize().y());
|
||||
}
|
||||
|
||||
void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func)
|
||||
void GuiMenu::addEntry(
|
||||
const char* name,
|
||||
unsigned int color,
|
||||
bool add_arrow,
|
||||
const std::function<void()>& func)
|
||||
{
|
||||
std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
|
||||
|
||||
// populate the list
|
||||
// Populate the list.
|
||||
ComponentListRow row;
|
||||
row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true);
|
||||
|
||||
if(add_arrow)
|
||||
{
|
||||
if (add_arrow) {
|
||||
std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow);
|
||||
row.addElement(bracket, false);
|
||||
}
|
||||
|
@ -628,11 +692,11 @@ void GuiMenu::addEntry(const char* name, unsigned int color, bool add_arrow, con
|
|||
|
||||
bool GuiMenu::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(GuiComponent::input(config, input))
|
||||
if (GuiComponent::input(config, input))
|
||||
return true;
|
||||
|
||||
if((config->isMappedTo("b", input) || config->isMappedTo("start", input)) && input.value != 0)
|
||||
{
|
||||
if ((config->isMappedTo("b", input) || config->isMappedTo("start", input)) &&
|
||||
input.value != 0) {
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GuiMenu.h
|
||||
//
|
||||
// Main menu.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_GUIS_GUI_MENU_H
|
||||
#define ES_APP_GUIS_GUI_MENU_H
|
||||
|
@ -16,7 +22,8 @@ public:
|
|||
HelpStyle getHelpStyle() override;
|
||||
|
||||
private:
|
||||
void addEntry(const char* name, unsigned int color, bool add_arrow, const std::function<void()>& func);
|
||||
void addEntry(const char* name, unsigned int color,
|
||||
bool add_arrow, const std::function<void()>& func);
|
||||
void addVersionInfo();
|
||||
void openCollectionSystemSettings();
|
||||
void openConfigInput();
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//
|
||||
// ViewController.cpp
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#include "views/ViewController.h"
|
||||
|
||||
#include "animations/Animation.h"
|
||||
|
@ -18,7 +27,7 @@
|
|||
#include "Window.h"
|
||||
#include "Sound.h"
|
||||
|
||||
ViewController* ViewController::sInstance = NULL;
|
||||
ViewController* ViewController::sInstance = nullptr;
|
||||
NavigationSounds navigationsounds;
|
||||
|
||||
ViewController* ViewController::get()
|
||||
|
@ -33,8 +42,13 @@ void ViewController::init(Window* window)
|
|||
sInstance = new ViewController(window);
|
||||
}
|
||||
|
||||
ViewController::ViewController(Window* window)
|
||||
: GuiComponent(window), mCurrentView(nullptr), mCamera(Transform4x4f::Identity()), mFadeOpacity(0), mLockInput(false)
|
||||
ViewController::ViewController(
|
||||
Window* window)
|
||||
: GuiComponent(window),
|
||||
mCurrentView(nullptr),
|
||||
mCamera(Transform4x4f::Identity()),
|
||||
mFadeOpacity(0),
|
||||
mLockInput(false)
|
||||
{
|
||||
mState.viewing = NOTHING;
|
||||
}
|
||||
|
@ -42,24 +56,23 @@ ViewController::ViewController(Window* window)
|
|||
ViewController::~ViewController()
|
||||
{
|
||||
assert(sInstance == this);
|
||||
sInstance = NULL;
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
void ViewController::goToStart()
|
||||
{
|
||||
// If specific system is requested, go directly to the game list
|
||||
// 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)
|
||||
{
|
||||
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
|
||||
// Requested system doesn't exist.
|
||||
Settings::getInstance()->setString("StartupSystem", "");
|
||||
}
|
||||
goToSystemView(SystemData::sSystemVector.at(0));
|
||||
|
@ -80,17 +93,16 @@ int ViewController::getSystemId(SystemData* system)
|
|||
|
||||
void ViewController::goToSystemView(SystemData* system)
|
||||
{
|
||||
// Tell any current view it's about to be hidden
|
||||
// Tell any current view it's about to be hidden.
|
||||
if (mCurrentView)
|
||||
{
|
||||
mCurrentView->onHide();
|
||||
}
|
||||
|
||||
mState.viewing = SYSTEM_SELECT;
|
||||
mState.system = system;
|
||||
|
||||
auto systemList = getSystemListView();
|
||||
systemList->setPosition(getSystemId(system) * (float)Renderer::getScreenWidth(), systemList->getPosition().y());
|
||||
systemList->setPosition(getSystemId(system) * (float)Renderer::getScreenWidth(),
|
||||
systemList->getPosition().y());
|
||||
|
||||
systemList->goToSystem(system, false);
|
||||
mCurrentView = systemList;
|
||||
|
@ -120,13 +132,14 @@ void ViewController::goToPrevGameList()
|
|||
|
||||
void ViewController::goToGameList(SystemData* system)
|
||||
{
|
||||
if(mState.viewing == SYSTEM_SELECT)
|
||||
{
|
||||
// move system list
|
||||
if (mState.viewing == SYSTEM_SELECT) {
|
||||
// Move system list.
|
||||
auto sysList = getSystemListView();
|
||||
float offX = sysList->getPosition().x();
|
||||
int sysId = getSystemId(system);
|
||||
sysList->setPosition(sysId * (float)Renderer::getScreenWidth(), sysList->getPosition().y());
|
||||
|
||||
sysList->setPosition(sysId * (float)Renderer::getScreenWidth(),
|
||||
sysList->getPosition().y());
|
||||
offX = sysList->getPosition().x() - offX;
|
||||
mCamera.translation().x() -= offX;
|
||||
}
|
||||
|
@ -135,67 +148,65 @@ void ViewController::goToGameList(SystemData* system)
|
|||
mState.system = system;
|
||||
|
||||
if (mCurrentView)
|
||||
{
|
||||
mCurrentView->onHide();
|
||||
}
|
||||
|
||||
mCurrentView = getGameListView(system);
|
||||
|
||||
if (mCurrentView)
|
||||
{
|
||||
mCurrentView->onShow();
|
||||
}
|
||||
playViewTransition();
|
||||
}
|
||||
|
||||
void ViewController::playViewTransition()
|
||||
{
|
||||
Vector3f target(Vector3f::Zero());
|
||||
if(mCurrentView)
|
||||
if (mCurrentView)
|
||||
target = mCurrentView->getPosition();
|
||||
|
||||
// no need to animate, we're not going anywhere (probably goToNextGamelist() or goToPrevGamelist() when there's only 1 system)
|
||||
if(target == -mCamera.translation() && !isAnimationPlaying(0))
|
||||
// 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
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
const static int FADE_DURATION = 240; // fade in/out time
|
||||
const static int FADE_WAIT = 320; // time to wait between in/out
|
||||
const static int FADE_DURATION = 240; // Fade in/out time.
|
||||
const static int FADE_WAIT = 320; // Time to wait between in/out.
|
||||
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
|
||||
// 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);
|
||||
advanceAnimation(0, FADE_DURATION - (int)(mFadeOpacity * FADE_DURATION));
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
advanceAnimation(0, (int)(mFadeOpacity * FADE_DURATION));
|
||||
}
|
||||
} else if (transition_style == "slide"){
|
||||
// slide or simple slide
|
||||
}
|
||||
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(); // Update help prompts immediately.
|
||||
}
|
||||
else {
|
||||
// Instant.
|
||||
setAnimation(new LambdaAnimation([this, target](float /*t*/) {
|
||||
this->mCamera.translation() = -target; }, 1));
|
||||
updateHelpPrompts();
|
||||
}
|
||||
}
|
||||
|
@ -203,19 +214,18 @@ void ViewController::playViewTransition()
|
|||
void ViewController::onFileChanged(FileData* file, FileChangeType change)
|
||||
{
|
||||
auto it = mGameListViews.find(file->getSystem());
|
||||
if(it != mGameListViews.cend())
|
||||
if (it != mGameListViews.cend())
|
||||
it->second->onFileChanged(file, change);
|
||||
}
|
||||
|
||||
void ViewController::launch(FileData* game, Vector3f center)
|
||||
{
|
||||
if(game->getType() != GAME)
|
||||
{
|
||||
if (game->getType() != GAME) {
|
||||
LOG(LogError) << "tried to launch something that isn't a game";
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the current view
|
||||
// Hide the current view.
|
||||
if (mCurrentView)
|
||||
mCurrentView->onHide();
|
||||
|
||||
|
@ -223,47 +233,51 @@ void ViewController::launch(FileData* game, Vector3f center)
|
|||
origCamera.translation() = -mCurrentView->getPosition();
|
||||
|
||||
center += mCurrentView->getPosition();
|
||||
stopAnimation(1); // make sure the fade in isn't still playing
|
||||
mWindow->stopInfoPopup(); // make sure we disable any existing info popup
|
||||
stopAnimation(1); // Make sure the fade in isn't still playing.
|
||||
mWindow->stopInfoPopup(); // Make sure we disable any existing info popup.
|
||||
mLockInput = true;
|
||||
|
||||
std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
|
||||
|
||||
navigationsounds.playThemeNavigationSound(LAUNCHSOUND);
|
||||
// let launch sound play to the end before launching game
|
||||
// Let launch sound play to the end before launching game.
|
||||
while(navigationsounds.isPlayingThemeNavigationSound(LAUNCHSOUND));
|
||||
|
||||
if(transition_style == "fade")
|
||||
{
|
||||
// fade out, launch game, fade back in
|
||||
if (transition_style == "fade") {
|
||||
// Fade out, launch game, fade back in.
|
||||
auto fadeFunc = [this](float t) {
|
||||
mFadeOpacity = Math::lerp(0.0f, 1.0f, t);
|
||||
};
|
||||
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game, fadeFunc]
|
||||
{
|
||||
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this, game, fadeFunc] {
|
||||
game->launchGame(mWindow);
|
||||
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] { mLockInput = false; }, true);
|
||||
setAnimation(new LambdaAnimation(fadeFunc, 800), 0, [this] {
|
||||
mLockInput = false; }, true);
|
||||
this->onFileChanged(game, FILE_METADATA_CHANGED);
|
||||
if (mCurrentView)
|
||||
mCurrentView->onShow();
|
||||
});
|
||||
} else if (transition_style == "slide"){
|
||||
// move camera to zoom in on center + fade out, launch game, come back in
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0, [this, origCamera, center, game]
|
||||
{
|
||||
}
|
||||
else if (transition_style == "slide") {
|
||||
// Move camera to zoom in on center + fade out, launch game, come back in.
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0,
|
||||
[this, origCamera, center, game] {
|
||||
game->launchGame(mWindow);
|
||||
mCamera = origCamera;
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] { mLockInput = false; }, true);
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 600), 0, [this] {
|
||||
mLockInput = false; }, true);
|
||||
this->onFileChanged(game, FILE_METADATA_CHANGED);
|
||||
if (mCurrentView)
|
||||
mCurrentView->onShow();
|
||||
});
|
||||
} else { // instant
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this, origCamera, center, game]
|
||||
{
|
||||
}
|
||||
// Instant
|
||||
else {
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0,
|
||||
[this, origCamera, center, game] {
|
||||
game->launchGame(mWindow);
|
||||
mCamera = origCamera;
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0, [this] { mLockInput = false; }, true);
|
||||
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 10), 0,
|
||||
[this] { mLockInput = false; }, true);
|
||||
this->onFileChanged(game, FILE_METADATA_CHANGED);
|
||||
if (mCurrentView)
|
||||
mCurrentView->onShow();
|
||||
|
@ -273,10 +287,8 @@ void ViewController::launch(FileData* game, Vector3f center)
|
|||
|
||||
void ViewController::removeGameListView(SystemData* system)
|
||||
{
|
||||
//if we already made one, return that one
|
||||
auto exists = mGameListViews.find(system);
|
||||
if(exists != mGameListViews.cend())
|
||||
{
|
||||
if (exists != mGameListViews.cend()) {
|
||||
exists->second.reset();
|
||||
mGameListViews.erase(system);
|
||||
}
|
||||
|
@ -284,18 +296,18 @@ void ViewController::removeGameListView(SystemData* system)
|
|||
|
||||
std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* system)
|
||||
{
|
||||
//if we already made one, return that one
|
||||
// If we already made one, return that one.
|
||||
auto exists = mGameListViews.find(system);
|
||||
if(exists != mGameListViews.cend())
|
||||
if (exists != mGameListViews.cend())
|
||||
return exists->second;
|
||||
|
||||
system->getIndex()->setUIModeFilters();
|
||||
//if we didn't, make it, remember it, and return it
|
||||
// If we didn't, make it, remember it, and return it.
|
||||
std::shared_ptr<IGameListView> view;
|
||||
|
||||
bool themeHasVideoView = system->getTheme()->hasView("video");
|
||||
|
||||
//decide type
|
||||
// Decide type.
|
||||
GameListViewType selectedViewType = AUTOMATIC;
|
||||
|
||||
std::string viewPreference = Settings::getInstance()->getString("GamelistViewStyle");
|
||||
|
@ -308,39 +320,39 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
|
|||
if (viewPreference.compare("video") == 0)
|
||||
selectedViewType = VIDEO;
|
||||
|
||||
if (selectedViewType == AUTOMATIC)
|
||||
{
|
||||
if (selectedViewType == AUTOMATIC) {
|
||||
std::vector<FileData*> files = system->getRootFolder()->getFilesRecursive(GAME | FOLDER);
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++)
|
||||
{
|
||||
if (themeHasVideoView && !(*it)->getVideoPath().empty())
|
||||
{
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++) {
|
||||
if (themeHasVideoView && !(*it)->getVideoPath().empty()) {
|
||||
selectedViewType = VIDEO;
|
||||
break;
|
||||
}
|
||||
else if (!(*it)->getThumbnailPath().empty())
|
||||
{
|
||||
else if (!(*it)->getThumbnailPath().empty()) {
|
||||
selectedViewType = DETAILED;
|
||||
// Don't break out in case any subsequent files have video
|
||||
// Don't break out in case any subsequent files have videos.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create the view
|
||||
// Create the view.
|
||||
switch (selectedViewType)
|
||||
{
|
||||
case VIDEO:
|
||||
view = std::shared_ptr<IGameListView>(new VideoGameListView(mWindow, system->getRootFolder()));
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new VideoGameListView(mWindow, system->getRootFolder()));
|
||||
break;
|
||||
case DETAILED:
|
||||
view = std::shared_ptr<IGameListView>(new DetailedGameListView(mWindow, system->getRootFolder()));
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new DetailedGameListView(mWindow, system->getRootFolder()));
|
||||
break;
|
||||
case GRID:
|
||||
view = std::shared_ptr<IGameListView>(new GridGameListView(mWindow, system->getRootFolder()));
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new GridGameListView(mWindow, system->getRootFolder()));
|
||||
break;
|
||||
case BASIC:
|
||||
default:
|
||||
view = std::shared_ptr<IGameListView>(new BasicGameListView(mWindow, system->getRootFolder()));
|
||||
view = std::shared_ptr<IGameListView>(
|
||||
new BasicGameListView(mWindow, system->getRootFolder()));
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -348,7 +360,8 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
|
|||
|
||||
std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
|
||||
int id = (int)(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin());
|
||||
view->setPosition(id * (float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight() * 2);
|
||||
view->setPosition(id * (float)Renderer::getScreenWidth(),
|
||||
(float)Renderer::getScreenHeight() * 2);
|
||||
|
||||
addChild(view.get());
|
||||
|
||||
|
@ -358,8 +371,8 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
|
|||
|
||||
std::shared_ptr<SystemView> ViewController::getSystemListView()
|
||||
{
|
||||
//if we already made one, return that one
|
||||
if(mSystemListView)
|
||||
// If we already made one, return that one.
|
||||
if (mSystemListView)
|
||||
return mSystemListView;
|
||||
|
||||
mSystemListView = std::shared_ptr<SystemView>(new SystemView(mWindow));
|
||||
|
@ -371,23 +384,22 @@ std::shared_ptr<SystemView> ViewController::getSystemListView()
|
|||
|
||||
bool ViewController::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(mLockInput)
|
||||
if (mLockInput)
|
||||
return true;
|
||||
|
||||
// open menu
|
||||
if(!(UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("DisableKidStartMenu")) && config->isMappedTo("start", input) && input.value != 0)
|
||||
{
|
||||
// open menu
|
||||
// Open menu.
|
||||
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
||||
Settings::getInstance()->getBool("DisableKidStartMenu")) &&
|
||||
config->isMappedTo("start", input) && input.value != 0) {
|
||||
mWindow->pushGui(new GuiMenu(mWindow));
|
||||
return true;
|
||||
}
|
||||
|
||||
if(UIModeController::getInstance()->listen(config, input)) // check if UI mode has changed due to passphrase completion
|
||||
{
|
||||
// Check if UI mode has changed due to passphrase completion.
|
||||
if (UIModeController::getInstance()->listen(config, input))
|
||||
return true;
|
||||
}
|
||||
|
||||
if(mCurrentView)
|
||||
if (mCurrentView)
|
||||
return mCurrentView->input(config, input);
|
||||
|
||||
return false;
|
||||
|
@ -395,10 +407,8 @@ bool ViewController::input(InputConfig* config, Input input)
|
|||
|
||||
void ViewController::update(int deltaTime)
|
||||
{
|
||||
if(mCurrentView)
|
||||
{
|
||||
if (mCurrentView)
|
||||
mCurrentView->update(deltaTime);
|
||||
}
|
||||
|
||||
updateSelf(deltaTime);
|
||||
}
|
||||
|
@ -409,48 +419,48 @@ void ViewController::render(const Transform4x4f& parentTrans)
|
|||
Transform4x4f transInverse;
|
||||
transInverse.invert(trans);
|
||||
|
||||
// camera position, position + size
|
||||
// Camera position, position + size.
|
||||
Vector3f viewStart = transInverse.translation();
|
||||
Vector3f viewEnd = transInverse * Vector3f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight(), 0);
|
||||
Vector3f viewEnd = transInverse * Vector3f((float)Renderer::getScreenWidth(),
|
||||
(float)Renderer::getScreenHeight(), 0);
|
||||
|
||||
// Keep track of UI mode changes.
|
||||
UIModeController::getInstance()->monitorUIMode();
|
||||
|
||||
// draw systemview
|
||||
// Draw system view.
|
||||
getSystemListView()->render(trans);
|
||||
|
||||
// draw gamelists
|
||||
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
|
||||
{
|
||||
// clipping
|
||||
// Draw gamelists.
|
||||
for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) {
|
||||
// Clipping.
|
||||
Vector3f guiStart = it->second->getPosition();
|
||||
Vector3f guiEnd = it->second->getPosition() + Vector3f(it->second->getSize().x(), it->second->getSize().y(), 0);
|
||||
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())
|
||||
if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
|
||||
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
|
||||
it->second->render(trans);
|
||||
}
|
||||
|
||||
if(mWindow->peekGui() == this)
|
||||
if (mWindow->peekGui() == this)
|
||||
mWindow->renderHelpPromptsEarly();
|
||||
|
||||
// fade out
|
||||
if(mFadeOpacity)
|
||||
{
|
||||
// Fade out.
|
||||
if (mFadeOpacity) {
|
||||
unsigned int fadeColor = 0x00000000 | (unsigned char)(mFadeOpacity * 255);
|
||||
Renderer::setMatrix(parentTrans);
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(), Renderer::getScreenHeight(), fadeColor, fadeColor);
|
||||
Renderer::drawRect(0.0f, 0.0f, Renderer::getScreenWidth(),
|
||||
Renderer::getScreenHeight(), fadeColor, fadeColor);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::preload()
|
||||
{
|
||||
uint32_t i = 0;
|
||||
for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++)
|
||||
{
|
||||
if(Settings::getInstance()->getBool("SplashScreen") &&
|
||||
Settings::getInstance()->getBool("SplashScreenProgress"))
|
||||
{
|
||||
for (auto it = SystemData::sSystemVector.cbegin();
|
||||
it != SystemData::sSystemVector.cend(); it++) {
|
||||
if (Settings::getInstance()->getBool("SplashScreen") &&
|
||||
Settings::getInstance()->getBool("SplashScreenProgress")) {
|
||||
i++;
|
||||
char buffer[100];
|
||||
sprintf (buffer, "Loading '%s' (%d/%d)",
|
||||
|
@ -461,80 +471,74 @@ void ViewController::preload()
|
|||
(*it)->getIndex()->resetFilters();
|
||||
getGameListView(*it);
|
||||
}
|
||||
// load navigation sounds
|
||||
// Load navigation sounds.
|
||||
navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme());
|
||||
}
|
||||
|
||||
void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
|
||||
{
|
||||
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
|
||||
{
|
||||
if(it->second.get() == view)
|
||||
{
|
||||
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)
|
||||
if (reloadTheme)
|
||||
system->loadTheme();
|
||||
system->getIndex()->setUIModeFilters();
|
||||
std::shared_ptr<IGameListView> newView = getGameListView(system);
|
||||
|
||||
// to counter having come from a placeholder
|
||||
// To counter having come from a placeholder.
|
||||
if (!cursor->isPlaceHolder()) {
|
||||
newView->setCursor(cursor);
|
||||
}
|
||||
if(isCurrent)
|
||||
if (isCurrent)
|
||||
mCurrentView = newView;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Redisplay the current view
|
||||
// Redisplay the current view.
|
||||
if (mCurrentView)
|
||||
mCurrentView->onShow();
|
||||
|
||||
}
|
||||
|
||||
void ViewController::reloadAll()
|
||||
{
|
||||
// clear all gamelistviews
|
||||
// Clear all GameListViews.
|
||||
std::map<SystemData*, FileData*> cursorMap;
|
||||
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
// 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
|
||||
// Rebuild SystemListView.
|
||||
mSystemListView.reset();
|
||||
getSystemListView();
|
||||
|
||||
// update mCurrentView since the pointers changed
|
||||
if(mState.viewing == GAME_LIST)
|
||||
{
|
||||
// Update mCurrentView since the pointers changed.
|
||||
if (mState.viewing == GAME_LIST) {
|
||||
mCurrentView = getGameListView(mState.getSystem());
|
||||
}else if(mState.viewing == SYSTEM_SELECT)
|
||||
{
|
||||
}
|
||||
else if (mState.viewing == SYSTEM_SELECT) {
|
||||
SystemData* system = mState.getSystem();
|
||||
goToSystemView(SystemData::sSystemVector.front());
|
||||
mSystemListView->goToSystem(system, false);
|
||||
mCurrentView = mSystemListView;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
goToSystemView(SystemData::sSystemVector.front());
|
||||
}
|
||||
|
||||
// load navigation sounds
|
||||
// Load navigation sounds.
|
||||
navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme());
|
||||
|
||||
updateHelpPrompts();
|
||||
|
@ -543,19 +547,19 @@ void ViewController::reloadAll()
|
|||
std::vector<HelpPrompt> ViewController::getHelpPrompts()
|
||||
{
|
||||
std::vector<HelpPrompt> prompts;
|
||||
if(!mCurrentView)
|
||||
if (!mCurrentView)
|
||||
return prompts;
|
||||
|
||||
prompts = mCurrentView->getHelpPrompts();
|
||||
if(!(UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("DisableKidStartMenu")))
|
||||
if (!(UIModeController::getInstance()->isUIModeKid() &&
|
||||
Settings::getInstance()->getBool("DisableKidStartMenu")))
|
||||
prompts.push_back(HelpPrompt("start", "menu"));
|
||||
|
||||
return prompts;
|
||||
}
|
||||
|
||||
HelpStyle ViewController::getHelpStyle()
|
||||
{
|
||||
if(!mCurrentView)
|
||||
if (!mCurrentView)
|
||||
return GuiComponent::getHelpStyle();
|
||||
|
||||
return mCurrentView->getHelpStyle();
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
//
|
||||
// ViewController.h
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H
|
||||
#define ES_APP_VIEWS_VIEW_CONTROLLER_H
|
||||
|
@ -11,7 +20,8 @@ class IGameListView;
|
|||
class SystemData;
|
||||
class SystemView;
|
||||
|
||||
// Used to smoothly transition the camera between multiple views (e.g. from system to system, from gamelist to gamelist).
|
||||
// Handles transitions between views, e.g. from system to system and from gamelist to gamelist.
|
||||
// Also sets up the initial gamelists and refreshes and reloads them as required.
|
||||
class ViewController : public GuiComponent
|
||||
{
|
||||
public:
|
||||
|
@ -27,8 +37,11 @@ public:
|
|||
// If a basic view detected a metadata change, it can request to recreate
|
||||
// the current gamelist view (as it may change to be detailed).
|
||||
void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false);
|
||||
inline void reloadGameListView(SystemData* system, bool reloadTheme = false) { reloadGameListView(getGameListView(system).get(), reloadTheme); }
|
||||
void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes.
|
||||
inline void reloadGameListView(SystemData* system, bool reloadTheme = false)
|
||||
{ reloadGameListView(getGameListView(system).get(), reloadTheme); }
|
||||
// Reload everything with a theme.
|
||||
// Used when the "ThemeSet" setting changes.
|
||||
void reloadAll();
|
||||
|
||||
// Navigation.
|
||||
void goToNextGameList();
|
||||
|
@ -42,22 +55,21 @@ public:
|
|||
|
||||
// Plays a nice launch effect and launches the game at the end of it.
|
||||
// Once the game terminates, plays a return effect.
|
||||
void launch(FileData* game, Vector3f centerCameraOn = Vector3f(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0));
|
||||
void launch(FileData* game, Vector3f centerCameraOn =
|
||||
Vector3f(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0));
|
||||
|
||||
bool input(InputConfig* config, Input input) override;
|
||||
void update(int deltaTime) override;
|
||||
void render(const Transform4x4f& parentTrans) override;
|
||||
|
||||
enum ViewMode
|
||||
{
|
||||
enum ViewMode {
|
||||
NOTHING,
|
||||
START_SCREEN,
|
||||
SYSTEM_SELECT,
|
||||
GAME_LIST
|
||||
};
|
||||
|
||||
enum GameListViewType
|
||||
{
|
||||
enum GameListViewType {
|
||||
AUTOMATIC,
|
||||
BASIC,
|
||||
DETAILED,
|
||||
|
@ -65,11 +77,11 @@ public:
|
|||
VIDEO
|
||||
};
|
||||
|
||||
struct State
|
||||
{
|
||||
struct State {
|
||||
ViewMode viewing;
|
||||
|
||||
inline SystemData* getSystem() const { assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; }
|
||||
inline SystemData* getSystem() const
|
||||
{ assert(viewing == GAME_LIST || viewing == SYSTEM_SELECT); return system; }
|
||||
|
||||
private:
|
||||
friend ViewController;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// BasicGameListView.cpp
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'Basic'.
|
||||
//
|
||||
|
||||
#include "views/gamelist/BasicGameListView.h"
|
||||
|
||||
#include "utils/FileSystemUtil.h"
|
||||
|
@ -7,8 +13,11 @@
|
|||
#include "Settings.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
BasicGameListView::BasicGameListView(Window* window, FileData* root)
|
||||
: ISimpleGameListView(window, root), mList(window)
|
||||
BasicGameListView::BasicGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: ISimpleGameListView(window, root),
|
||||
mList(window)
|
||||
{
|
||||
mList.setSize(mSize.x(), mSize.y() * 0.8f);
|
||||
mList.setPosition(0, mSize.y() * 0.2f);
|
||||
|
@ -29,9 +38,9 @@ void BasicGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
|
||||
void BasicGameListView::onFileChanged(FileData* file, FileChangeType change)
|
||||
{
|
||||
if(change == FILE_METADATA_CHANGED)
|
||||
if (change == FILE_METADATA_CHANGED)
|
||||
{
|
||||
// might switch to a detailed view
|
||||
// Might switch to a detailed view.
|
||||
ViewController::get()->reloadGameListView(this);
|
||||
return;
|
||||
}
|
||||
|
@ -43,55 +52,23 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files)
|
|||
{
|
||||
mList.clear();
|
||||
mHeaderText.setText(mRoot->getSystem()->getFullName());
|
||||
if (files.size() > 0)
|
||||
{
|
||||
|
||||
std::string systemName = mRoot->getSystem()->getName();
|
||||
|
||||
bool favoritesFirst = Settings::getInstance()->getBool("FavoritesFirst");
|
||||
|
||||
bool showFavoriteIcon = (systemName != "favorites" && systemName != "recent");
|
||||
if (!showFavoriteIcon)
|
||||
favoritesFirst = false;
|
||||
|
||||
if (favoritesFirst)
|
||||
{
|
||||
for (auto file : files)
|
||||
{
|
||||
if (!file->getFavorite())
|
||||
continue;
|
||||
|
||||
if (showFavoriteIcon)
|
||||
mList.add(FAVORITE_GAME_CHAR + " " + file->getName(), file, file->getType() == FOLDER);
|
||||
else if (file->getType() == FOLDER)
|
||||
mList.add(FAVORITE_FOLDER_CHAR + " " + file->getName(), file, true);
|
||||
else
|
||||
mList.add(file->getName(), file, false);
|
||||
if (files.size() > 0) {
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++) {
|
||||
if ((*it)->getFavorite() &&
|
||||
mRoot->getSystem()->getName() != "favorites") {
|
||||
mList.add(FAVORITE_GAME_CHAR + " " + (*it)->getName(),
|
||||
*it, ((*it)->getType() == FOLDER));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto file : files)
|
||||
{
|
||||
if (file->getFavorite())
|
||||
{
|
||||
if (favoritesFirst)
|
||||
continue;
|
||||
|
||||
if (showFavoriteIcon)
|
||||
{
|
||||
mList.add(FAVORITE_GAME_CHAR + " " + file->getName(), file, file->getType() == FOLDER);
|
||||
continue;
|
||||
}
|
||||
else if ((*it)->getType() == FOLDER &&
|
||||
mRoot->getSystem()->getName() != "collections") {
|
||||
mList.add(FAVORITE_FOLDER_CHAR + " " + (*it)->getName(), *it, true);
|
||||
}
|
||||
else {
|
||||
mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER));
|
||||
}
|
||||
|
||||
if (file->getType() == FOLDER)
|
||||
mList.add(FAVORITE_FOLDER_CHAR + " " + file->getName(), file, true);
|
||||
else
|
||||
mList.add(file->getName(), file, false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
addPlaceholder();
|
||||
}
|
||||
}
|
||||
|
@ -103,25 +80,25 @@ FileData* BasicGameListView::getCursor()
|
|||
|
||||
void BasicGameListView::setCursor(FileData* cursor)
|
||||
{
|
||||
if(!mList.setCursor(cursor) && (!cursor->isPlaceHolder()))
|
||||
{
|
||||
if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) {
|
||||
populateList(cursor->getParent()->getChildrenListToDisplay());
|
||||
mList.setCursor(cursor);
|
||||
|
||||
// update our cursor stack in case our cursor just got set to some folder we weren't in before
|
||||
if(mCursorStack.empty() || mCursorStack.top() != cursor->getParent())
|
||||
// Update our cursor stack in case our cursor just
|
||||
// got set to some folder we weren't in before.
|
||||
if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent())
|
||||
{
|
||||
std::stack<FileData*> tmp;
|
||||
FileData* ptr = cursor->getParent();
|
||||
while(ptr && ptr != mRoot)
|
||||
while (ptr && ptr != mRoot)
|
||||
{
|
||||
tmp.push(ptr);
|
||||
ptr = ptr->getParent();
|
||||
}
|
||||
|
||||
// flip the stack and put it in mCursorStack
|
||||
// Flip the stack and put it in mCursorStack.
|
||||
mCursorStack = std::stack<FileData*>();
|
||||
while(!tmp.empty())
|
||||
while (!tmp.empty())
|
||||
{
|
||||
mCursorStack.push(tmp.top());
|
||||
tmp.pop();
|
||||
|
@ -132,8 +109,9 @@ void BasicGameListView::setCursor(FileData* cursor)
|
|||
|
||||
void BasicGameListView::addPlaceholder()
|
||||
{
|
||||
// empty list - add a placeholder
|
||||
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
|
||||
// Empty list - add a placeholder.
|
||||
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>",
|
||||
this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
|
||||
mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER));
|
||||
}
|
||||
|
||||
|
@ -154,47 +132,47 @@ void BasicGameListView::launch(FileData* game)
|
|||
|
||||
void BasicGameListView::remove(FileData *game, bool deleteFile)
|
||||
{
|
||||
// Actually delete the file on the filesystem.
|
||||
if (deleteFile)
|
||||
Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem
|
||||
Utils::FileSystem::removeFile(game->getPath());
|
||||
|
||||
FileData* parent = game->getParent();
|
||||
if (getCursor() == game) // Select next element in list, or prev if none
|
||||
{
|
||||
// Select next element in list, or previous if none.
|
||||
if (getCursor() == game) {
|
||||
std::vector<FileData*> siblings = parent->getChildrenListToDisplay();
|
||||
auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game);
|
||||
unsigned int gamePos = (int)std::distance(siblings.cbegin(), gameIter);
|
||||
if (gameIter != siblings.cend())
|
||||
{
|
||||
if (gameIter != siblings.cend()) {
|
||||
if ((gamePos + 1) < siblings.size())
|
||||
{
|
||||
setCursor(siblings.at(gamePos + 1));
|
||||
} else if (gamePos > 1) {
|
||||
else if (gamePos > 1)
|
||||
setCursor(siblings.at(gamePos - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
mList.remove(game);
|
||||
if(mList.size() == 0)
|
||||
{
|
||||
|
||||
if (mList.size() == 0)
|
||||
addPlaceholder();
|
||||
}
|
||||
delete game; // remove before repopulating (removes from parent)
|
||||
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed
|
||||
|
||||
// Remove before repopulating (removes from parent), then update the view.
|
||||
delete game;
|
||||
onFileChanged(parent, FILE_REMOVED);
|
||||
}
|
||||
|
||||
std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
|
||||
{
|
||||
std::vector<HelpPrompt> prompts;
|
||||
|
||||
if(Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
prompts.push_back(HelpPrompt("left/right", "system"));
|
||||
prompts.push_back(HelpPrompt("up/down", "choose"));
|
||||
prompts.push_back(HelpPrompt("a", "launch"));
|
||||
prompts.push_back(HelpPrompt("b", "back"));
|
||||
if(!UIModeController::getInstance()->isUIModeKid())
|
||||
if (!UIModeController::getInstance()->isUIModeKid())
|
||||
prompts.push_back(HelpPrompt("select", "options"));
|
||||
if(mRoot->getSystem()->isGameSystem())
|
||||
if (mRoot->getSystem()->isGameSystem())
|
||||
prompts.push_back(HelpPrompt("x", "random"));
|
||||
if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid())
|
||||
if (mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid())
|
||||
{
|
||||
std::string prompt = CollectionSystemManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// BasicGameListView.h
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'basic'.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H
|
||||
|
@ -10,7 +16,7 @@ class BasicGameListView : public ISimpleGameListView
|
|||
public:
|
||||
BasicGameListView(Window* window, FileData* root);
|
||||
|
||||
// Called when a FileData* is added, has its metadata changed, or is removed
|
||||
// Called when a FileData* is added, has its metadata changed, or is removed.
|
||||
virtual void onFileChanged(FileData* file, FileChangeType change);
|
||||
|
||||
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme);
|
||||
|
|
|
@ -1,24 +1,44 @@
|
|||
//
|
||||
// DetailedGameListView.cpp
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'detailed'.
|
||||
//
|
||||
|
||||
#include "views/gamelist/DetailedGameListView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
#include "views/ViewController.h"
|
||||
|
||||
DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
||||
BasicGameListView(window, root),
|
||||
mDescContainer(window), mDescription(window),
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
DetailedGameListView::DetailedGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: BasicGameListView(window, root),
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
|
||||
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window),
|
||||
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window),
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
|
||||
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window),
|
||||
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window),
|
||||
mName(window)
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window)
|
||||
{
|
||||
//mHeaderImage.setPosition(mSize.x() * 0.25f, 0);
|
||||
|
||||
const float padding = 0.01f;
|
||||
|
||||
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y());
|
||||
|
@ -26,7 +46,7 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
|
||||
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
|
||||
|
||||
// Thumbnail
|
||||
// Thumbnail.
|
||||
mThumbnail.setOrigin(0.5f, 0.5f);
|
||||
mThumbnail.setPosition(2.0f, 2.0f);
|
||||
mThumbnail.setVisible(false);
|
||||
|
@ -34,23 +54,23 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
mThumbnail.setDefaultZIndex(25);
|
||||
addChild(&mThumbnail);
|
||||
|
||||
// Marquee
|
||||
// Marquee.
|
||||
mMarquee.setOrigin(0.5f, 0.5f);
|
||||
// Default to off the screen
|
||||
// Default to off the screen.
|
||||
mMarquee.setPosition(2.0f, 2.0f);
|
||||
mMarquee.setVisible(false);
|
||||
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
|
||||
mMarquee.setDefaultZIndex(35);
|
||||
addChild(&mMarquee);
|
||||
|
||||
// image
|
||||
// Image.
|
||||
mImage.setOrigin(0.5f, 0.5f);
|
||||
mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f);
|
||||
mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f);
|
||||
mImage.setDefaultZIndex(30);
|
||||
addChild(&mImage);
|
||||
|
||||
// metadata labels + values
|
||||
// Metadata labels + values.
|
||||
mLblRating.setText("Rating: ");
|
||||
addChild(&mLblRating);
|
||||
addChild(&mRating);
|
||||
|
@ -85,7 +105,8 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -94,7 +115,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
|
|||
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||
mDescContainer.addChild(&mDescription);
|
||||
|
||||
|
||||
initMDLabels();
|
||||
initMDValues();
|
||||
updateInfoPanel();
|
||||
|
@ -105,9 +125,12 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
BasicGameListView::onThemeChanged(theme);
|
||||
|
||||
using namespace ThemeFlags;
|
||||
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mName.applyTheme(theme, getName(), "md_name", ALL);
|
||||
|
||||
initMDLabels();
|
||||
|
@ -118,11 +141,8 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
}
|
||||
|
||||
|
||||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
|
@ -132,14 +152,14 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
|
|||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < values.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
}
|
||||
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description", POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||
mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
sortChildren();
|
||||
}
|
||||
|
@ -156,15 +176,14 @@ void DetailedGameListView::initMDLabels()
|
|||
const float colSize = (mSize.x() * 0.48f) / colCount;
|
||||
const float rowPadding = 0.01f * mSize.y();
|
||||
|
||||
for(unsigned int i = 0; i < components.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < components.size(); i++) {
|
||||
const unsigned int row = i % rowCount;
|
||||
Vector3f pos(0.0f, 0.0f, 0.0f);
|
||||
if(row == 0)
|
||||
{
|
||||
if (row == 0) {
|
||||
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
|
||||
}else{
|
||||
// work from the last component
|
||||
}
|
||||
else {
|
||||
// Work from the last component.
|
||||
GuiComponent* lc = components[i-1];
|
||||
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
|
||||
}
|
||||
|
@ -193,20 +212,22 @@ void DetailedGameListView::initMDValues()
|
|||
float bottom = 0.0f;
|
||||
|
||||
const float colSize = (mSize.x() * 0.48f) / 2;
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++) {
|
||||
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2;
|
||||
values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||
values[i]->setPosition(labels[i]->getPosition() +
|
||||
Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
|
||||
values[i]->setDefaultZIndex(40);
|
||||
|
||||
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
|
||||
if(testBot > bottom)
|
||||
|
||||
if (testBot > bottom)
|
||||
bottom = testBot;
|
||||
}
|
||||
|
||||
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
void DetailedGameListView::updateInfoPanel()
|
||||
|
@ -214,12 +235,12 @@ void DetailedGameListView::updateInfoPanel()
|
|||
FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected();
|
||||
|
||||
bool fadingOut;
|
||||
if(file == NULL)
|
||||
{
|
||||
if (file == nullptr) {
|
||||
//mImage.setImage("");
|
||||
//mDescription.setText("");
|
||||
fadingOut = true;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
mThumbnail.setImage(file->getThumbnailPath());
|
||||
mMarquee.setImage(file->getMarqueePath());
|
||||
mImage.setImage(file->getImagePath());
|
||||
|
@ -234,8 +255,7 @@ void DetailedGameListView::updateInfoPanel()
|
|||
mPlayers.setValue(file->metadata.get("players"));
|
||||
mName.setValue(file->metadata.get("name"));
|
||||
|
||||
if(file->getType() == GAME)
|
||||
{
|
||||
if (file->getType() == GAME) {
|
||||
mLastPlayed.setValue(file->metadata.get("lastplayed"));
|
||||
mPlayCount.setValue(file->metadata.get("playcount"));
|
||||
}
|
||||
|
@ -252,18 +272,13 @@ void DetailedGameListView::updateInfoPanel()
|
|||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
|
||||
|
||||
for(auto it = comps.cbegin(); it != comps.cend(); it++)
|
||||
{
|
||||
for (auto it = comps.cbegin(); it != comps.cend(); it++) {
|
||||
GuiComponent* comp = *it;
|
||||
// an animation is playing
|
||||
// then animate if reverse != fadingOut
|
||||
// an animation is not playing
|
||||
// then animate if opacity != our target opacity
|
||||
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255)))
|
||||
{
|
||||
auto func = [comp](float t)
|
||||
{
|
||||
// An animation is playing, then animate if reverse != fadingOut
|
||||
// An animation is not playing, then animate if opacity != our target opacity
|
||||
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
|
||||
auto func = [comp](float t) {
|
||||
comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
|
||||
};
|
||||
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
|
||||
|
@ -274,7 +289,7 @@ void DetailedGameListView::updateInfoPanel()
|
|||
void DetailedGameListView::launch(FileData* game)
|
||||
{
|
||||
Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0);
|
||||
if(mImage.hasImage())
|
||||
if (mImage.hasImage())
|
||||
target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
|
||||
|
||||
ViewController::get()->launch(game, target);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// DetailedGameListView.h
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'detailed'.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H
|
||||
|
@ -28,7 +34,14 @@ private:
|
|||
ImageComponent mMarquee;
|
||||
ImageComponent mImage;
|
||||
|
||||
TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount;
|
||||
TextComponent mLblRating;
|
||||
TextComponent mLblReleaseDate;
|
||||
TextComponent mLblDeveloper;
|
||||
TextComponent mLblPublisher;
|
||||
TextComponent mLblGenre;
|
||||
TextComponent mLblPlayers;
|
||||
TextComponent mLblLastPlayed;
|
||||
TextComponent mLblPlayCount;
|
||||
|
||||
RatingComponent mRating;
|
||||
DateTimeComponent mReleaseDate;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GridGameListView.cpp
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'grid'.
|
||||
//
|
||||
|
||||
#include "views/gamelist/GridGameListView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
|
@ -12,24 +18,42 @@
|
|||
#endif
|
||||
#include "components/VideoVlcComponent.h"
|
||||
|
||||
GridGameListView::GridGameListView(Window* window, FileData* root) :
|
||||
ISimpleGameListView(window, root),
|
||||
mGrid(window), mMarquee(window),
|
||||
mImage(window),
|
||||
mVideo(nullptr),
|
||||
mVideoPlaying(false),
|
||||
mDescContainer(window), mDescription(window),
|
||||
GridGameListView::GridGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: ISimpleGameListView(window, root),
|
||||
|
||||
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window),
|
||||
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window),
|
||||
mGrid(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
mVideo(nullptr),
|
||||
mVideoPlaying(false),
|
||||
|
||||
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window),
|
||||
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window),
|
||||
mName(window)
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window)
|
||||
{
|
||||
const float padding = 0.01f;
|
||||
|
||||
// Create the correct type of video window
|
||||
// Create the correct type of video window.
|
||||
#ifdef _RPI_
|
||||
if (Settings::getInstance()->getBool("VideoOmxPlayer"))
|
||||
mVideo = new VideoPlayerComponent(window, "");
|
||||
|
@ -46,7 +70,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
|
|||
|
||||
populateList(root->getChildrenListToDisplay());
|
||||
|
||||
// metadata labels + values
|
||||
// Metadata labels + values.
|
||||
mLblRating.setText("Rating: ");
|
||||
addChild(&mLblRating);
|
||||
addChild(&mRating);
|
||||
|
@ -81,7 +105,8 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -89,7 +114,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
|
|||
mDescription.setFont(Font::get(FONT_SIZE_SMALL));
|
||||
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||
mDescContainer.addChild(&mDescription);
|
||||
|
||||
|
||||
mMarquee.setOrigin(0.5f, 0.5f);
|
||||
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
|
||||
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
|
||||
|
@ -128,7 +153,7 @@ FileData* GridGameListView::getCursor()
|
|||
|
||||
void GridGameListView::setCursor(FileData* file)
|
||||
{
|
||||
if(!mGrid.setCursor(file))
|
||||
if (!mGrid.setCursor(file))
|
||||
{
|
||||
populateList(file->getParent()->getChildrenListToDisplay());
|
||||
mGrid.setCursor(file);
|
||||
|
@ -147,11 +172,13 @@ std::string GridGameListView::getQuickSystemSelectLeftButton()
|
|||
|
||||
bool GridGameListView::input(InputConfig* config, Input input)
|
||||
{
|
||||
if (input.value == 0 and (config->isMappedLike("left", input) || config->isMappedLike("right", input)
|
||||
|| (config->isMappedLike("up", input)) || (config->isMappedLike("down", input)) ))
|
||||
if (input.value == 0 and (config->isMappedLike("left", input) ||
|
||||
config->isMappedLike("right", input) ||
|
||||
(config->isMappedLike("up", input)) ||
|
||||
(config->isMappedLike("down", input)) ))
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
|
||||
if(config->isMappedLike("left", input) || config->isMappedLike("right", input))
|
||||
if (config->isMappedLike("left", input) || config->isMappedLike("right", input))
|
||||
return GuiComponent::input(config, input);
|
||||
|
||||
return ISimpleGameListView::input(config, input);
|
||||
|
@ -167,8 +194,8 @@ const std::string GridGameListView::getImagePath(FileData* file)
|
|||
else if (src == ImageSource::MARQUEE)
|
||||
return file->getMarqueePath();
|
||||
|
||||
// If no thumbnail was found, then use the image media type
|
||||
if(file->getThumbnailPath() == "");
|
||||
// If no thumbnail was found, then use the image media type.
|
||||
if (file->getThumbnailPath() == "");
|
||||
return file->getImagePath();
|
||||
|
||||
return file->getThumbnailPath();
|
||||
|
@ -178,17 +205,12 @@ void GridGameListView::populateList(const std::vector<FileData*>& files)
|
|||
{
|
||||
mGrid.clear();
|
||||
mHeaderText.setText(mRoot->getSystem()->getFullName());
|
||||
if (files.size() > 0)
|
||||
{
|
||||
if (files.size() > 0) {
|
||||
for (auto it = files.cbegin(); it != files.cend(); it++)
|
||||
{
|
||||
mGrid.add((*it)->getName(), getImagePath(*it), *it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
addPlaceholder();
|
||||
}
|
||||
}
|
||||
|
||||
void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
||||
|
@ -199,9 +221,12 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
|
||||
mGrid.applyTheme(theme, getName(), "gamegrid", ALL);
|
||||
mName.applyTheme(theme, getName(), "md_name", ALL);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mVideo->applyTheme(theme, getName(), "md_video",
|
||||
POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE);
|
||||
|
||||
initMDLabels();
|
||||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
|
@ -211,11 +236,8 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
}
|
||||
|
||||
|
||||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
|
@ -225,16 +247,17 @@ void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < values.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
}
|
||||
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description", POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||
mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
// Repopulate list in case new theme is displaying a different image. Preserve selection.
|
||||
// Repopulate list in case a new theme is displaying a different image.
|
||||
// Preserve selection.
|
||||
FileData* file = mGrid.getSelected();
|
||||
populateList(mRoot->getChildrenListToDisplay());
|
||||
mGrid.setCursor(file);
|
||||
|
@ -254,15 +277,14 @@ void GridGameListView::initMDLabels()
|
|||
const float colSize = (mSize.x() * 0.48f) / colCount;
|
||||
const float rowPadding = 0.01f * mSize.y();
|
||||
|
||||
for(unsigned int i = 0; i < components.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < components.size(); i++) {
|
||||
const unsigned int row = i % rowCount;
|
||||
Vector3f pos(0.0f, 0.0f, 0.0f);
|
||||
if(row == 0)
|
||||
{
|
||||
if (row == 0) {
|
||||
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
|
||||
}else{
|
||||
// work from the last component
|
||||
}
|
||||
else {
|
||||
// Work from the last component.
|
||||
GuiComponent* lc = components[i-1];
|
||||
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
|
||||
}
|
||||
|
@ -291,36 +313,37 @@ void GridGameListView::initMDValues()
|
|||
float bottom = 0.0f;
|
||||
|
||||
const float colSize = (mSize.x() * 0.48f) / 2;
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++) {
|
||||
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2;
|
||||
values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||
values[i]->setPosition(labels[i]->getPosition() +
|
||||
Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
|
||||
values[i]->setDefaultZIndex(40);
|
||||
|
||||
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
|
||||
if(testBot > bottom)
|
||||
if (testBot > bottom)
|
||||
bottom = testBot;
|
||||
}
|
||||
|
||||
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
void GridGameListView::updateInfoPanel()
|
||||
{
|
||||
FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? NULL : mGrid.getSelected();
|
||||
FileData* file = (mGrid.size() == 0 || mGrid.isScrolling()) ? nullptr : mGrid.getSelected();
|
||||
|
||||
bool fadingOut;
|
||||
if(file == NULL)
|
||||
if (file == nullptr)
|
||||
{
|
||||
mVideo->setVideo("");
|
||||
mVideo->setImage("");
|
||||
mVideoPlaying = false;
|
||||
|
||||
//mDescription.setText("");
|
||||
fadingOut = true;
|
||||
}else{
|
||||
// Temporary fix to disable only audio from playing
|
||||
// if (!mVideo->setVideo(file->getVideoPath()))
|
||||
// {
|
||||
// mVideo->setDefaultVideo();
|
||||
|
@ -330,7 +353,7 @@ void GridGameListView::updateInfoPanel()
|
|||
// mVideo->setImage(file->getThumbnailPath());
|
||||
mMarquee.setImage(file->getMarqueePath());
|
||||
// mImage.setImage(file->getImagePath());
|
||||
|
||||
|
||||
mDescription.setText(file->metadata.get("desc"));
|
||||
mDescContainer.reset();
|
||||
|
||||
|
@ -342,8 +365,7 @@ void GridGameListView::updateInfoPanel()
|
|||
mPlayers.setValue(file->metadata.get("players"));
|
||||
mName.setValue(file->metadata.get("name"));
|
||||
|
||||
if(file->getType() == GAME)
|
||||
{
|
||||
if (file->getType() == GAME) {
|
||||
mLastPlayed.setValue(file->metadata.get("lastplayed"));
|
||||
mPlayCount.setValue(file->metadata.get("playcount"));
|
||||
}
|
||||
|
@ -360,18 +382,13 @@ void GridGameListView::updateInfoPanel()
|
|||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
|
||||
|
||||
for(auto it = comps.cbegin(); it != comps.cend(); it++)
|
||||
{
|
||||
for (auto it = comps.cbegin(); it != comps.cend(); it++) {
|
||||
GuiComponent* comp = *it;
|
||||
// an animation is playing
|
||||
// then animate if reverse != fadingOut
|
||||
// an animation is not playing
|
||||
// then animate if opacity != our target opacity
|
||||
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255)))
|
||||
{
|
||||
auto func = [comp](float t)
|
||||
{
|
||||
// An animation is playing, then animate if reverse != fadingOut
|
||||
// An animation is not playing, then animate if opacity != our target opacity
|
||||
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
|
||||
auto func = [comp](float t) {
|
||||
comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
|
||||
};
|
||||
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
|
||||
|
@ -381,67 +398,62 @@ void GridGameListView::updateInfoPanel()
|
|||
|
||||
void GridGameListView::addPlaceholder()
|
||||
{
|
||||
// empty grid - add a placeholder
|
||||
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
|
||||
// Empty grid - add a placeholder.
|
||||
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>",
|
||||
this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
|
||||
mGrid.add(placeholder->getName(), "", placeholder);
|
||||
}
|
||||
|
||||
void GridGameListView::launch(FileData* game)
|
||||
{
|
||||
{
|
||||
float screenWidth = (float) Renderer::getScreenWidth();
|
||||
float screenHeight = (float) Renderer::getScreenHeight();
|
||||
|
||||
Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0);
|
||||
|
||||
if(mMarquee.hasImage() &&
|
||||
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
|
||||
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
|
||||
{
|
||||
if (mMarquee.hasImage() &&
|
||||
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
|
||||
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
|
||||
target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0);
|
||||
}
|
||||
else if(mImage.hasImage() &&
|
||||
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
|
||||
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
|
||||
{
|
||||
else if (mImage.hasImage() &&
|
||||
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
|
||||
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
|
||||
target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
|
||||
}
|
||||
else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
|
||||
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
|
||||
{
|
||||
else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
|
||||
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
|
||||
target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0);
|
||||
}
|
||||
|
||||
ViewController::get()->launch(game, target);
|
||||
|
||||
}
|
||||
|
||||
void GridGameListView::remove(FileData *game, bool deleteFile)
|
||||
{
|
||||
// Actually delete the file on the filesystem.
|
||||
if (deleteFile)
|
||||
Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem
|
||||
Utils::FileSystem::removeFile(game->getPath());
|
||||
|
||||
FileData* parent = game->getParent();
|
||||
if (getCursor() == game) // Select next element in list, or prev if none
|
||||
{
|
||||
// Select next element in list, or previous if none.
|
||||
if (getCursor() == game) {
|
||||
std::vector<FileData*> siblings = parent->getChildrenListToDisplay();
|
||||
auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game);
|
||||
int gamePos = (int)std::distance(siblings.cbegin(), gameIter);
|
||||
if (gameIter != siblings.cend())
|
||||
{
|
||||
if (gameIter != siblings.cend()) {
|
||||
if ((gamePos + 1) < (int)siblings.size())
|
||||
{
|
||||
setCursor(siblings.at(gamePos + 1));
|
||||
} else if ((gamePos - 1) > 0) {
|
||||
else if ((gamePos - 1) > 0)
|
||||
setCursor(siblings.at(gamePos - 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
mGrid.remove(game);
|
||||
if(mGrid.size() == 0)
|
||||
{
|
||||
|
||||
if (mGrid.size() == 0)
|
||||
addPlaceholder();
|
||||
}
|
||||
delete game; // remove before repopulating (removes from parent)
|
||||
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed
|
||||
|
||||
// Remove before repopulating (removes from parent).
|
||||
// Update the view, with game removed.
|
||||
delete game;
|
||||
onFileChanged(parent, FILE_REMOVED);
|
||||
}
|
||||
|
||||
std::vector<TextComponent*> GridGameListView::getMDLabels()
|
||||
|
@ -476,17 +488,16 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
|
|||
{
|
||||
std::vector<HelpPrompt> prompts;
|
||||
|
||||
if(Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
prompts.push_back(HelpPrompt("lr", "system"));
|
||||
prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
|
||||
prompts.push_back(HelpPrompt("a", "launch"));
|
||||
prompts.push_back(HelpPrompt("b", "back"));
|
||||
if(!UIModeController::getInstance()->isUIModeKid())
|
||||
if (!UIModeController::getInstance()->isUIModeKid())
|
||||
prompts.push_back(HelpPrompt("select", "options"));
|
||||
if(mRoot->getSystem()->isGameSystem())
|
||||
if (mRoot->getSystem()->isGameSystem())
|
||||
prompts.push_back(HelpPrompt("x", "random"));
|
||||
if(mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid())
|
||||
{
|
||||
if (mRoot->getSystem()->isGameSystem() && !UIModeController::getInstance()->isUIModeKid()) {
|
||||
std::string prompt = CollectionSystemManager::get()->getEditingCollection();
|
||||
prompts.push_back(HelpPrompt("y", prompt));
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GridGameListView.h
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'grid'.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
|
||||
|
@ -46,7 +52,14 @@ private:
|
|||
void initMDLabels();
|
||||
void initMDValues();
|
||||
|
||||
TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount;
|
||||
TextComponent mLblRating;
|
||||
TextComponent mLblReleaseDate;
|
||||
TextComponent mLblDeveloper;
|
||||
TextComponent mLblPublisher;
|
||||
TextComponent mLblGenre;
|
||||
TextComponent mLblPlayers;
|
||||
TextComponent mLblLastPlayed;
|
||||
TextComponent mLblPlayCount;
|
||||
|
||||
ImageComponent mMarquee;
|
||||
VideoComponent* mVideo;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// IGameListView.cpp
|
||||
//
|
||||
// Interface that defines the minimum for a GameListView.
|
||||
//
|
||||
|
||||
#include "views/gamelist/IGameListView.h"
|
||||
|
||||
#include "guis/GuiGamelistOptions.h"
|
||||
|
@ -8,16 +14,17 @@
|
|||
|
||||
bool IGameListView::input(InputConfig* config, Input input)
|
||||
{
|
||||
// select to open GuiGamelistOptions
|
||||
if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && input.value)
|
||||
{
|
||||
// Select button opens GuiGamelistOptions.
|
||||
if (!UIModeController::getInstance()->isUIModeKid() &&
|
||||
config->isMappedTo("select", input) && input.value) {
|
||||
mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem()));
|
||||
return true;
|
||||
|
||||
// Ctrl-R to reload a view when debugging
|
||||
}else if(Settings::getInstance()->getBool("Debug") && config->getDeviceId() == DEVICE_KEYBOARD &&
|
||||
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r && input.value != 0)
|
||||
{
|
||||
}
|
||||
// Ctrl-R reloads the view when debugging.
|
||||
else if (Settings::getInstance()->getBool("Debug") &&
|
||||
config->getDeviceId() == DEVICE_KEYBOARD &&
|
||||
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) &&
|
||||
input.id == SDLK_r && input.value != 0) {
|
||||
LOG(LogDebug) << "reloading view";
|
||||
ViewController::get()->reloadGameListView(this, true);
|
||||
return true;
|
||||
|
@ -46,8 +53,10 @@ void IGameListView::render(const Transform4x4f& parentTrans)
|
|||
float scaleX = trans.r0().x();
|
||||
float scaleY = trans.r1().y();
|
||||
|
||||
Vector2i pos((int)Math::round(trans.translation()[0]), (int)Math::round(trans.translation()[1]));
|
||||
Vector2i size((int)Math::round(mSize.x() * scaleX), (int)Math::round(mSize.y() * scaleY));
|
||||
Vector2i pos((int)Math::round(trans.translation()[0]),
|
||||
(int)Math::round(trans.translation()[1]));
|
||||
Vector2i size((int)Math::round(mSize.x() * scaleX),
|
||||
(int)Math::round(mSize.y() * scaleY));
|
||||
|
||||
Renderer::pushClipRect(pos, size);
|
||||
renderChildren(trans);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// IGameListView.h
|
||||
//
|
||||
// Interface that defines the minimum for a GameListView.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H
|
||||
|
@ -13,12 +19,18 @@ class Window;
|
|||
class IGameListView : public GuiComponent
|
||||
{
|
||||
public:
|
||||
IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root)
|
||||
{ setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); }
|
||||
IGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: GuiComponent(window),
|
||||
mRoot(root)
|
||||
{ setSize((float)Renderer::getScreenWidth(),
|
||||
(float)Renderer::getScreenHeight()); }
|
||||
|
||||
virtual ~IGameListView() {}
|
||||
|
||||
// Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted.
|
||||
// Called when a new file is added, a file is removed, a file's metadata changes,
|
||||
// or a file's children are sorted.
|
||||
// NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started.
|
||||
// Since sorts are recursive, that FileData's children probably changed too.
|
||||
virtual void onFileChanged(FileData* file, FileChangeType change) = 0;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// ISimpleGameListView.cpp
|
||||
//
|
||||
// Interface that defines a simple GameListView.
|
||||
//
|
||||
|
||||
#include "views/gamelist/ISimpleGameListView.h"
|
||||
|
||||
#include "views/UIModeController.h"
|
||||
|
@ -7,8 +13,13 @@
|
|||
#include "Sound.h"
|
||||
#include "SystemData.h"
|
||||
|
||||
ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root),
|
||||
mHeaderText(window), mHeaderImage(window), mBackground(window)
|
||||
ISimpleGameListView::ISimpleGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: IGameListView(window, root),
|
||||
mHeaderText(window),
|
||||
mHeaderImage(window),
|
||||
mBackground(window)
|
||||
{
|
||||
mHeaderText.setText("Logo Text");
|
||||
mHeaderText.setSize(mSize.x(), 0);
|
||||
|
@ -35,7 +46,7 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
|
|||
mHeaderImage.applyTheme(theme, getName(), "logo", ALL);
|
||||
mHeaderText.applyTheme(theme, getName(), "logoText", ALL);
|
||||
|
||||
// Remove old theme extras
|
||||
// Remove old theme extras.
|
||||
for (auto extra : mThemeExtras)
|
||||
{
|
||||
removeChild(extra);
|
||||
|
@ -43,18 +54,16 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
|
|||
}
|
||||
mThemeExtras.clear();
|
||||
|
||||
// Add new theme extras
|
||||
// Add new theme extras.
|
||||
mThemeExtras = ThemeData::makeExtras(theme, getName(), mWindow);
|
||||
for (auto extra : mThemeExtras)
|
||||
{
|
||||
addChild(extra);
|
||||
}
|
||||
|
||||
if(mHeaderImage.hasImage())
|
||||
{
|
||||
if (mHeaderImage.hasImage()) {
|
||||
removeChild(&mHeaderText);
|
||||
addChild(&mHeaderImage);
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
addChild(&mHeaderText);
|
||||
removeChild(&mHeaderImage);
|
||||
}
|
||||
|
@ -62,8 +71,8 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
|
|||
|
||||
void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*change*/)
|
||||
{
|
||||
// we could be tricky here to be efficient;
|
||||
// but this shouldn't happen very often so we'll just always repopulate
|
||||
// We could be tricky here to be efficient;
|
||||
// but this shouldn't happen very often so we'll just always repopulate.
|
||||
FileData* cursor = getCursor();
|
||||
if (!cursor->isPlaceHolder()) {
|
||||
populateList(cursor->getParent()->getChildrenListToDisplay());
|
||||
|
@ -78,20 +87,17 @@ void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*cha
|
|||
|
||||
bool ISimpleGameListView::input(InputConfig* config, Input input)
|
||||
{
|
||||
std::shared_ptr<Sound> soundfile;
|
||||
std::shared_ptr<Sound> soundfile;
|
||||
|
||||
if(input.value != 0)
|
||||
{
|
||||
if(config->isMappedTo("a", input))
|
||||
{
|
||||
if (input.value != 0) {
|
||||
if (config->isMappedTo("a", input)) {
|
||||
FileData* cursor = getCursor();
|
||||
if(cursor->getType() == GAME)
|
||||
{
|
||||
if (cursor->getType() == GAME) {
|
||||
launch(cursor);
|
||||
}else{
|
||||
// it's a folder
|
||||
if(cursor->getChildren().size() > 0)
|
||||
{
|
||||
}
|
||||
else {
|
||||
// It's a folder.
|
||||
if (cursor->getChildren().size() > 0) {
|
||||
mCursorStack.push(cursor);
|
||||
populateList(cursor->getChildrenListToDisplay());
|
||||
FileData* cursor = getCursor();
|
||||
|
@ -100,14 +106,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
|
||||
return true;
|
||||
}else if(config->isMappedTo("b", input))
|
||||
{
|
||||
if(mCursorStack.size())
|
||||
{
|
||||
}
|
||||
else if (config->isMappedTo("b", input)) {
|
||||
if (mCursorStack.size()) {
|
||||
populateList(mCursorStack.top()->getParent()->getChildren());
|
||||
setCursor(mCursorStack.top());
|
||||
mCursorStack.pop();
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
navigationsounds.playThemeNavigationSound(BACKSOUND);
|
||||
onFocusLost();
|
||||
SystemData* systemToView = getCursor()->getSystem();
|
||||
|
@ -119,57 +125,40 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
|
|||
}
|
||||
|
||||
return true;
|
||||
}else if(config->isMappedLike(getQuickSystemSelectRightButton(), input))
|
||||
{
|
||||
if(Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
{
|
||||
}
|
||||
else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect")) {
|
||||
onFocusLost();
|
||||
ViewController::get()->goToNextGameList();
|
||||
return true;
|
||||
}
|
||||
}else if(config->isMappedLike(getQuickSystemSelectLeftButton(), input))
|
||||
{
|
||||
if(Settings::getInstance()->getBool("QuickSystemSelect"))
|
||||
{
|
||||
}
|
||||
else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
|
||||
if (Settings::getInstance()->getBool("QuickSystemSelect")) {
|
||||
onFocusLost();
|
||||
ViewController::get()->goToPrevGameList();
|
||||
return true;
|
||||
}
|
||||
}else if (config->isMappedTo("x", input))
|
||||
{
|
||||
if (mRoot->getSystem()->isGameSystem())
|
||||
{
|
||||
// go to random system game
|
||||
}
|
||||
else if (config->isMappedTo("x", input)) {
|
||||
if (mRoot->getSystem()->isGameSystem()) {
|
||||
// Go to random system game.
|
||||
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
|
||||
FileData* randomGame = getCursor()->getSystem()->getRandomGame();
|
||||
if (randomGame)
|
||||
{
|
||||
setCursor(randomGame);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}else if (config->isMappedTo("y", input) && !UIModeController::getInstance()->isUIModeKid())
|
||||
{
|
||||
if(mRoot->getSystem()->isGameSystem())
|
||||
{
|
||||
}
|
||||
else if (config->isMappedTo("y", input) &&
|
||||
!UIModeController::getInstance()->isUIModeKid()) {
|
||||
if (mRoot->getSystem()->isGameSystem()) {
|
||||
navigationsounds.playThemeNavigationSound(FAVORITESOUND);
|
||||
if(CollectionSystemManager::get()->toggleGameInCollection(getCursor()))
|
||||
{
|
||||
if (CollectionSystemManager::get()->toggleGameInCollection(getCursor()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return IGameListView::input(config, input);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// ISimpleGameListView.h
|
||||
//
|
||||
// Interface that defines a simple GameListView.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H
|
||||
|
@ -11,9 +17,11 @@ class ISimpleGameListView : public IGameListView
|
|||
{
|
||||
public:
|
||||
ISimpleGameListView(Window* window, FileData* root);
|
||||
|
||||
virtual ~ISimpleGameListView() {}
|
||||
|
||||
// Called when a new file is added, a file is removed, a file's metadata changes, or a file's children are sorted.
|
||||
// Called when a new file is added, a file is removed, a file's metadata changes,
|
||||
// or a file's children are sorted.
|
||||
// NOTE: FILE_SORTED is only reported for the topmost FileData, where the sort started.
|
||||
// Since sorts are recursive, that FileData's children probably changed too.
|
||||
virtual void onFileChanged(FileData* file, FileChangeType change);
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// VideoGameListView.cpp
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'video'.
|
||||
//
|
||||
|
||||
#include "views/gamelist/VideoGameListView.h"
|
||||
|
||||
#include "animations/LambdaAnimation.h"
|
||||
|
@ -11,25 +17,41 @@
|
|||
#include "Settings.h"
|
||||
#endif
|
||||
|
||||
VideoGameListView::VideoGameListView(Window* window, FileData* root) :
|
||||
BasicGameListView(window, root),
|
||||
mDescContainer(window), mDescription(window),
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
mVideo(nullptr),
|
||||
mVideoPlaying(false),
|
||||
VideoGameListView::VideoGameListView(
|
||||
Window* window,
|
||||
FileData* root)
|
||||
: BasicGameListView(window, root),
|
||||
mDescContainer(window),
|
||||
mDescription(window),
|
||||
|
||||
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window),
|
||||
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window),
|
||||
mThumbnail(window),
|
||||
mMarquee(window),
|
||||
mImage(window),
|
||||
mVideo(nullptr),
|
||||
mVideoPlaying(false),
|
||||
|
||||
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window),
|
||||
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window),
|
||||
mName(window)
|
||||
mLblRating(window),
|
||||
mLblReleaseDate(window),
|
||||
mLblDeveloper(window),
|
||||
mLblPublisher(window),
|
||||
mLblGenre(window),
|
||||
mLblPlayers(window),
|
||||
mLblLastPlayed(window),
|
||||
mLblPlayCount(window),
|
||||
|
||||
mRating(window),
|
||||
mReleaseDate(window),
|
||||
mDeveloper(window),
|
||||
mPublisher(window),
|
||||
mGenre(window),
|
||||
mPlayers(window),
|
||||
mLastPlayed(window),
|
||||
mPlayCount(window),
|
||||
mName(window)
|
||||
{
|
||||
const float padding = 0.01f;
|
||||
|
||||
// Create the correct type of video window
|
||||
// Create the correct type of video window.
|
||||
#ifdef _RPI_
|
||||
if (Settings::getInstance()->getBool("VideoOmxPlayer"))
|
||||
mVideo = new VideoPlayerComponent(window, "");
|
||||
|
@ -44,7 +66,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
|
|||
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
|
||||
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
|
||||
|
||||
// Thumbnail
|
||||
// Thumbnail.
|
||||
mThumbnail.setOrigin(0.5f, 0.5f);
|
||||
mThumbnail.setPosition(2.0f, 2.0f);
|
||||
mThumbnail.setVisible(false);
|
||||
|
@ -52,30 +74,30 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
|
|||
mThumbnail.setDefaultZIndex(35);
|
||||
addChild(&mThumbnail);
|
||||
|
||||
// Marquee
|
||||
// Marquee.
|
||||
mMarquee.setOrigin(0.5f, 0.5f);
|
||||
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
|
||||
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
|
||||
mMarquee.setDefaultZIndex(35);
|
||||
addChild(&mMarquee);
|
||||
|
||||
// Image
|
||||
// Image.
|
||||
mImage.setOrigin(0.5f, 0.5f);
|
||||
// Default to off the screen
|
||||
// Default to off the screen.
|
||||
mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f);
|
||||
mImage.setVisible(false);
|
||||
mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f);
|
||||
mImage.setDefaultZIndex(30);
|
||||
addChild(&mImage);
|
||||
|
||||
// video
|
||||
// Video.
|
||||
mVideo->setOrigin(0.5f, 0.5f);
|
||||
mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f);
|
||||
mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f);
|
||||
mVideo->setDefaultZIndex(30);
|
||||
addChild(mVideo);
|
||||
|
||||
// metadata labels + values
|
||||
// Metadata labels + values.
|
||||
mLblRating.setText("Rating: ");
|
||||
addChild(&mLblRating);
|
||||
addChild(&mRating);
|
||||
|
@ -110,7 +132,8 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
|
|||
addChild(&mName);
|
||||
|
||||
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f);
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mSize.x() * (0.50f - 2*padding), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
mDescContainer.setAutoScroll(true);
|
||||
mDescContainer.setDefaultZIndex(40);
|
||||
addChild(&mDescContainer);
|
||||
|
@ -133,10 +156,14 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
BasicGameListView::onThemeChanged(theme);
|
||||
|
||||
using namespace ThemeFlags;
|
||||
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE);
|
||||
mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mMarquee.applyTheme(theme, getName(), "md_marquee",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mImage.applyTheme(theme, getName(), "md_image",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
|
||||
mVideo->applyTheme(theme, getName(), "md_video",
|
||||
POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE);
|
||||
mName.applyTheme(theme, getName(), "md_name", ALL);
|
||||
|
||||
initMDLabels();
|
||||
|
@ -147,11 +174,8 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
"md_lbl_genre", "md_lbl_players", "md_lbl_lastplayed", "md_lbl_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++)
|
||||
labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
|
||||
}
|
||||
|
||||
|
||||
initMDValues();
|
||||
std::vector<GuiComponent*> values = getMDValues();
|
||||
|
@ -161,14 +185,14 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
|
|||
"md_genre", "md_players", "md_lastplayed", "md_playcount"
|
||||
};
|
||||
|
||||
for(unsigned int i = 0; i < values.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < values.size(); i++)
|
||||
values[i]->applyTheme(theme, getName(), valElements[i], ALL ^ ThemeFlags::TEXT);
|
||||
}
|
||||
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description", POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescContainer.applyTheme(theme, getName(), "md_description",
|
||||
POSITION | ThemeFlags::SIZE | Z_INDEX | VISIBLE);
|
||||
mDescription.setSize(mDescContainer.getSize().x(), 0);
|
||||
mDescription.applyTheme(theme, getName(), "md_description", ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
mDescription.applyTheme(theme, getName(), "md_description",
|
||||
ALL ^ (POSITION | ThemeFlags::SIZE | ThemeFlags::ORIGIN | TEXT | ROTATION));
|
||||
|
||||
sortChildren();
|
||||
}
|
||||
|
@ -185,15 +209,14 @@ void VideoGameListView::initMDLabels()
|
|||
const float colSize = (mSize.x() * 0.48f) / colCount;
|
||||
const float rowPadding = 0.01f * mSize.y();
|
||||
|
||||
for(unsigned int i = 0; i < components.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < components.size(); i++) {
|
||||
const unsigned int row = i % rowCount;
|
||||
Vector3f pos(0.0f, 0.0f, 0.0f);
|
||||
if(row == 0)
|
||||
{
|
||||
if (row == 0) {
|
||||
pos = start + Vector3f(colSize * (i / rowCount), 0, 0);
|
||||
}else{
|
||||
// work from the last component
|
||||
}
|
||||
else {
|
||||
// Work from the last component.
|
||||
GuiComponent* lc = components[i-1];
|
||||
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
|
||||
}
|
||||
|
@ -222,24 +245,23 @@ void VideoGameListView::initMDValues()
|
|||
float bottom = 0.0f;
|
||||
|
||||
const float colSize = (mSize.x() * 0.48f) / 2;
|
||||
for(unsigned int i = 0; i < labels.size(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < labels.size(); i++) {
|
||||
const float heightDiff = (labels[i]->getSize().y() - values[i]->getSize().y()) / 2;
|
||||
values[i]->setPosition(labels[i]->getPosition() + Vector3f(labels[i]->getSize().x(), heightDiff, 0));
|
||||
values[i]->setPosition(labels[i]->getPosition() +
|
||||
Vector3f(labels[i]->getSize().x(),heightDiff, 0));
|
||||
values[i]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
|
||||
values[i]->setDefaultZIndex(40);
|
||||
|
||||
float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
|
||||
if(testBot > bottom)
|
||||
if (testBot > bottom)
|
||||
bottom = testBot;
|
||||
}
|
||||
|
||||
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f);
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() - mDescContainer.getPosition().y());
|
||||
mDescContainer.setSize(mDescContainer.getSize().x(), mSize.y() -
|
||||
mDescContainer.getPosition().y());
|
||||
}
|
||||
|
||||
|
||||
|
||||
void VideoGameListView::updateInfoPanel()
|
||||
{
|
||||
FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected();
|
||||
|
@ -247,8 +269,7 @@ void VideoGameListView::updateInfoPanel()
|
|||
Utils::FileSystem::removeFile(getTitlePath());
|
||||
|
||||
bool fadingOut;
|
||||
if(file == NULL)
|
||||
{
|
||||
if (file == nullptr) {
|
||||
mVideo->setVideo("");
|
||||
mVideo->setImage("");
|
||||
mVideoPlaying = false;
|
||||
|
@ -256,14 +277,13 @@ void VideoGameListView::updateInfoPanel()
|
|||
//mDescription.setText("");
|
||||
fadingOut = true;
|
||||
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
if (!mVideo->setVideo(file->getVideoPath()))
|
||||
{
|
||||
mVideo->setDefaultVideo();
|
||||
}
|
||||
|
||||
mVideoPlaying = true;
|
||||
|
||||
// mVideo->setImage(file->getThumbnailPath());
|
||||
mVideo->setImage(file->getImagePath());
|
||||
mThumbnail.setImage(file->getThumbnailPath());
|
||||
mMarquee.setImage(file->getMarqueePath());
|
||||
|
@ -280,8 +300,7 @@ void VideoGameListView::updateInfoPanel()
|
|||
mPlayers.setValue(file->metadata.get("players"));
|
||||
mName.setValue(file->metadata.get("name"));
|
||||
|
||||
if(file->getType() == GAME)
|
||||
{
|
||||
if (file->getType() == GAME) {
|
||||
mLastPlayed.setValue(file->metadata.get("lastplayed"));
|
||||
mPlayCount.setValue(file->metadata.get("playcount"));
|
||||
}
|
||||
|
@ -299,18 +318,13 @@ void VideoGameListView::updateInfoPanel()
|
|||
std::vector<TextComponent*> labels = getMDLabels();
|
||||
comps.insert(comps.cend(), labels.cbegin(), labels.cend());
|
||||
|
||||
for(auto it = comps.cbegin(); it != comps.cend(); it++)
|
||||
{
|
||||
for (auto it = comps.cbegin(); it != comps.cend(); it++) {
|
||||
GuiComponent* comp = *it;
|
||||
// an animation is playing
|
||||
// then animate if reverse != fadingOut
|
||||
// an animation is not playing
|
||||
// then animate if opacity != our target opacity
|
||||
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255)))
|
||||
{
|
||||
auto func = [comp](float t)
|
||||
{
|
||||
// An animation is playing, then animate if reverse != fadingOut
|
||||
// An animation is not playing, then animate if opacity != our target opacity
|
||||
if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
|
||||
(!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
|
||||
auto func = [comp](float t) {
|
||||
comp->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
|
||||
};
|
||||
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
|
||||
|
@ -325,35 +339,31 @@ void VideoGameListView::launch(FileData* game)
|
|||
|
||||
Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0);
|
||||
|
||||
if(mMarquee.hasImage() &&
|
||||
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
|
||||
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
|
||||
{
|
||||
if (mMarquee.hasImage() &&
|
||||
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
|
||||
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
|
||||
target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0);
|
||||
}
|
||||
else if(mThumbnail.hasImage() &&
|
||||
(mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f &&
|
||||
mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f))
|
||||
{
|
||||
|
||||
else if (mThumbnail.hasImage() &&
|
||||
(mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f &&
|
||||
mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f))
|
||||
target = Vector3f(mThumbnail.getCenter().x(), mThumbnail.getCenter().y(), 0);
|
||||
}
|
||||
else if(mImage.hasImage() &&
|
||||
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
|
||||
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
|
||||
{
|
||||
|
||||
else if (mImage.hasImage() &&
|
||||
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
|
||||
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
|
||||
target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
|
||||
}
|
||||
else if(mHeaderImage.hasImage() &&
|
||||
(mHeaderImage.getPosition().x() < screenWidth && mHeaderImage.getPosition().x() > 0.0f &&
|
||||
mHeaderImage.getPosition().y() < screenHeight && mHeaderImage.getPosition().y() > 0.0f))
|
||||
{
|
||||
|
||||
else if (mHeaderImage.hasImage() &&
|
||||
(mHeaderImage.getPosition().x() < screenWidth &&
|
||||
mHeaderImage.getPosition().x() > 0.0f &&
|
||||
mHeaderImage.getPosition().y() < screenHeight &&
|
||||
mHeaderImage.getPosition().y() > 0.0f))
|
||||
target = Vector3f(mHeaderImage.getCenter().x(), mHeaderImage.getCenter().y(), 0);
|
||||
}
|
||||
else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
|
||||
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
|
||||
{
|
||||
|
||||
else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
|
||||
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
|
||||
target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0);
|
||||
}
|
||||
|
||||
ViewController::get()->launch(game, target);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// VideoGameListView.h
|
||||
//
|
||||
// Interface that defines a GameListView of the type 'video'.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H
|
||||
#define ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H
|
||||
|
@ -36,7 +42,14 @@ private:
|
|||
VideoComponent* mVideo;
|
||||
ImageComponent mImage;
|
||||
|
||||
TextComponent mLblRating, mLblReleaseDate, mLblDeveloper, mLblPublisher, mLblGenre, mLblPlayers, mLblLastPlayed, mLblPlayCount;
|
||||
TextComponent mLblRating;
|
||||
TextComponent mLblReleaseDate;
|
||||
TextComponent mLblDeveloper;
|
||||
TextComponent mLblPublisher;
|
||||
TextComponent mLblGenre;
|
||||
TextComponent mLblPlayers;
|
||||
TextComponent mLblLastPlayed;
|
||||
TextComponent mLblPlayCount;
|
||||
|
||||
RatingComponent mRating;
|
||||
DateTimeComponent mReleaseDate;
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GuiComponent.cpp
|
||||
//
|
||||
// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation.
|
||||
//
|
||||
|
||||
#include "GuiComponent.h"
|
||||
|
||||
#include "animations/Animation.h"
|
||||
|
@ -8,11 +14,19 @@
|
|||
#include "Window.h"
|
||||
#include <algorithm>
|
||||
|
||||
GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL), mOpacity(255),
|
||||
mPosition(Vector3f::Zero()), mOrigin(Vector2f::Zero()), mRotationOrigin(0.5, 0.5),
|
||||
mSize(Vector2f::Zero()), mTransform(Transform4x4f::Identity()), mIsProcessing(false), mVisible(true)
|
||||
GuiComponent::GuiComponent(Window* window)
|
||||
: mWindow(window),
|
||||
mParent(NULL),
|
||||
mOpacity(255),
|
||||
mPosition(Vector3f::Zero()),
|
||||
mOrigin(Vector2f::Zero()),
|
||||
mRotationOrigin(0.5, 0.5),
|
||||
mSize(Vector2f::Zero()),
|
||||
mTransform(Transform4x4f::Identity()),
|
||||
mIsProcessing(false),
|
||||
mVisible(true)
|
||||
{
|
||||
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
mAnimationMap[i] = NULL;
|
||||
}
|
||||
|
||||
|
@ -22,18 +36,17 @@ GuiComponent::~GuiComponent()
|
|||
|
||||
cancelAllAnimations();
|
||||
|
||||
if(mParent)
|
||||
if (mParent)
|
||||
mParent->removeChild(this);
|
||||
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->setParent(NULL);
|
||||
}
|
||||
|
||||
bool GuiComponent::input(InputConfig* config, Input input)
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
{
|
||||
if(getChild(i)->input(config, input))
|
||||
for (unsigned int i = 0; i < getChildCount(); i++) {
|
||||
if (getChild(i)->input(config, input))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -42,16 +55,14 @@ bool GuiComponent::input(InputConfig* config, Input input)
|
|||
|
||||
void GuiComponent::updateSelf(int deltaTime)
|
||||
{
|
||||
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
advanceAnimation(i, deltaTime);
|
||||
}
|
||||
|
||||
void GuiComponent::updateChildren(int deltaTime)
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->update(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiComponent::update(int deltaTime)
|
||||
|
@ -71,10 +82,8 @@ void GuiComponent::render(const Transform4x4f& parentTrans)
|
|||
|
||||
void GuiComponent::renderChildren(const Transform4x4f& transform) const
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
{
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->render(transform);
|
||||
}
|
||||
}
|
||||
|
||||
Vector3f GuiComponent::getPosition() const
|
||||
|
@ -175,12 +184,12 @@ Vector2f GuiComponent::getCenter() const
|
|||
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
|
||||
}
|
||||
|
||||
//Children stuff.
|
||||
// Children stuff.
|
||||
void GuiComponent::addChild(GuiComponent* cmp)
|
||||
{
|
||||
mChildren.push_back(cmp);
|
||||
|
||||
if(cmp->getParent())
|
||||
if (cmp->getParent())
|
||||
cmp->getParent()->removeChild(cmp);
|
||||
|
||||
cmp->setParent(this);
|
||||
|
@ -188,20 +197,16 @@ void GuiComponent::addChild(GuiComponent* cmp)
|
|||
|
||||
void GuiComponent::removeChild(GuiComponent* cmp)
|
||||
{
|
||||
if(!cmp->getParent())
|
||||
if (!cmp->getParent())
|
||||
return;
|
||||
|
||||
if(cmp->getParent() != this)
|
||||
{
|
||||
if (cmp->getParent() != this)
|
||||
LOG(LogError) << "Tried to remove child from incorrect parent!";
|
||||
}
|
||||
|
||||
cmp->setParent(NULL);
|
||||
|
||||
for(auto i = mChildren.cbegin(); i != mChildren.cend(); i++)
|
||||
{
|
||||
if(*i == cmp)
|
||||
{
|
||||
for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) {
|
||||
if (*i == cmp) {
|
||||
mChildren.erase(i);
|
||||
return;
|
||||
}
|
||||
|
@ -215,7 +220,7 @@ void GuiComponent::clearChildren()
|
|||
|
||||
void GuiComponent::sortChildren()
|
||||
{
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), [](GuiComponent* a, GuiComponent* b) {
|
||||
std::stable_sort(mChildren.begin(), mChildren.end(), [](GuiComponent* a, GuiComponent* b) {
|
||||
return b->getZIndex() > a->getZIndex();
|
||||
});
|
||||
}
|
||||
|
@ -248,10 +253,8 @@ unsigned char GuiComponent::getOpacity() const
|
|||
void GuiComponent::setOpacity(unsigned char opacity)
|
||||
{
|
||||
mOpacity = opacity;
|
||||
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
{
|
||||
for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
|
||||
(*it)->setOpacity(opacity);
|
||||
}
|
||||
}
|
||||
|
||||
const Transform4x4f& GuiComponent::getTransform()
|
||||
|
@ -259,28 +262,26 @@ const Transform4x4f& GuiComponent::getTransform()
|
|||
mTransform = Transform4x4f::Identity();
|
||||
mTransform.translate(mPosition);
|
||||
if (mScale != 1.0)
|
||||
{
|
||||
mTransform.scale(mScale);
|
||||
}
|
||||
if (mRotation != 0.0)
|
||||
{
|
||||
if (mRotation != 0.0) {
|
||||
// Calculate offset as difference between origin and rotation origin
|
||||
Vector2f rotationSize = getRotationSize();
|
||||
float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x();
|
||||
float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y();
|
||||
|
||||
// transform to offset point
|
||||
// Transform to offset point
|
||||
if (xOff != 0.0 || yOff != 0.0)
|
||||
mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f));
|
||||
|
||||
// apply rotation transform
|
||||
// Apply rotation transform
|
||||
mTransform.rotateZ(mRotation);
|
||||
|
||||
// Tranform back to original point
|
||||
if (xOff != 0.0 || yOff != 0.0)
|
||||
mTransform.translate(Vector3f(xOff, yOff, 0.0f));
|
||||
}
|
||||
mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1, mOrigin.y() * mSize.y() * -1, 0.0f));
|
||||
mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1,
|
||||
mOrigin.y() * mSize.y() * -1, 0.0f));
|
||||
return mTransform;
|
||||
}
|
||||
|
||||
|
@ -295,32 +296,31 @@ std::string GuiComponent::getValue() const
|
|||
|
||||
void GuiComponent::textInput(const char* text)
|
||||
{
|
||||
for(auto iter = mChildren.cbegin(); iter != mChildren.cend(); iter++)
|
||||
{
|
||||
for (auto iter = mChildren.cbegin(); iter != mChildren.cend(); iter++)
|
||||
(*iter)->textInput(text);
|
||||
}
|
||||
}
|
||||
|
||||
void GuiComponent::setAnimation(Animation* anim, int delay, std::function<void()> finishedCallback, bool reverse, unsigned char slot)
|
||||
void GuiComponent::setAnimation(Animation* anim, int delay,
|
||||
std::function<void()> finishedCallback, bool reverse, unsigned char slot)
|
||||
{
|
||||
assert(slot < MAX_ANIMATIONS);
|
||||
|
||||
AnimationController* oldAnim = mAnimationMap[slot];
|
||||
mAnimationMap[slot] = new AnimationController(anim, delay, finishedCallback, reverse);
|
||||
|
||||
if(oldAnim)
|
||||
if (oldAnim)
|
||||
delete oldAnim;
|
||||
}
|
||||
|
||||
bool GuiComponent::stopAnimation(unsigned char slot)
|
||||
{
|
||||
assert(slot < MAX_ANIMATIONS);
|
||||
if(mAnimationMap[slot])
|
||||
{
|
||||
if (mAnimationMap[slot]) {
|
||||
delete mAnimationMap[slot];
|
||||
mAnimationMap[slot] = NULL;
|
||||
return true;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -328,13 +328,13 @@ bool GuiComponent::stopAnimation(unsigned char slot)
|
|||
bool GuiComponent::cancelAnimation(unsigned char slot)
|
||||
{
|
||||
assert(slot < MAX_ANIMATIONS);
|
||||
if(mAnimationMap[slot])
|
||||
{
|
||||
if (mAnimationMap[slot]) {
|
||||
mAnimationMap[slot]->removeFinishedCallback();
|
||||
delete mAnimationMap[slot];
|
||||
mAnimationMap[slot] = NULL;
|
||||
return true;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -342,16 +342,17 @@ bool GuiComponent::cancelAnimation(unsigned char slot)
|
|||
bool GuiComponent::finishAnimation(unsigned char slot)
|
||||
{
|
||||
assert(slot < MAX_ANIMATIONS);
|
||||
if(mAnimationMap[slot])
|
||||
{
|
||||
// skip to animation's end
|
||||
const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]->getAnimation()->getDuration() - mAnimationMap[slot]->getTime());
|
||||
if (mAnimationMap[slot]) {
|
||||
// Skip to animation's end
|
||||
const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]->
|
||||
getAnimation()->getDuration() - mAnimationMap[slot]->getTime());
|
||||
assert(done);
|
||||
|
||||
delete mAnimationMap[slot]; // will also call finishedCallback
|
||||
delete mAnimationMap[slot]; // Will also call finishedCallback
|
||||
mAnimationMap[slot] = NULL;
|
||||
return true;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -360,29 +361,28 @@ bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time)
|
|||
{
|
||||
assert(slot < MAX_ANIMATIONS);
|
||||
AnimationController* anim = mAnimationMap[slot];
|
||||
if(anim)
|
||||
{
|
||||
if (anim) {
|
||||
bool done = anim->update(time);
|
||||
if(done)
|
||||
{
|
||||
if (done) {
|
||||
mAnimationMap[slot] = NULL;
|
||||
delete anim;
|
||||
}
|
||||
return true;
|
||||
}else{
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void GuiComponent::stopAllAnimations()
|
||||
{
|
||||
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
stopAnimation(i);
|
||||
}
|
||||
|
||||
void GuiComponent::cancelAllAnimations()
|
||||
{
|
||||
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||
cancelAnimation(i);
|
||||
}
|
||||
|
||||
|
@ -403,41 +403,44 @@ int GuiComponent::getAnimationTime(unsigned char slot) const
|
|||
return mAnimationMap[slot]->getTime();
|
||||
}
|
||||
|
||||
void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
||||
void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
const std::string& view, const std::string& element, unsigned int properties)
|
||||
{
|
||||
Vector2f scale = getParent() ? getParent()->getSize() : Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
Vector2f scale = getParent() ? getParent()->getSize()
|
||||
: Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||
|
||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "");
|
||||
if(!elem)
|
||||
if (!elem)
|
||||
return;
|
||||
|
||||
using namespace ThemeFlags;
|
||||
if(properties & POSITION && elem->has("pos"))
|
||||
{
|
||||
if (properties & POSITION && elem->has("pos")) {
|
||||
Vector2f denormalized = elem->get<Vector2f>("pos") * scale;
|
||||
setPosition(Vector3f(denormalized.x(), denormalized.y(), 0));
|
||||
}
|
||||
|
||||
if(properties & ThemeFlags::SIZE && elem->has("size"))
|
||||
if (properties & ThemeFlags::SIZE && elem->has("size"))
|
||||
setSize(elem->get<Vector2f>("size") * scale);
|
||||
|
||||
// position + size also implies origin
|
||||
if((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) && elem->has("origin"))
|
||||
// Position + size also implies origin
|
||||
if ((properties & ORIGIN || (properties & POSITION &&
|
||||
properties & ThemeFlags::SIZE)) && elem->has("origin")) {
|
||||
setOrigin(elem->get<Vector2f>("origin"));
|
||||
}
|
||||
|
||||
if(properties & ThemeFlags::ROTATION) {
|
||||
if(elem->has("rotation"))
|
||||
if (properties & ThemeFlags::ROTATION) {
|
||||
if (elem->has("rotation"))
|
||||
setRotationDegrees(elem->get<float>("rotation"));
|
||||
if(elem->has("rotationOrigin"))
|
||||
if (elem->has("rotationOrigin"))
|
||||
setRotationOrigin(elem->get<Vector2f>("rotationOrigin"));
|
||||
}
|
||||
|
||||
if(properties & ThemeFlags::Z_INDEX && elem->has("zIndex"))
|
||||
if (properties & ThemeFlags::Z_INDEX && elem->has("zIndex"))
|
||||
setZIndex(elem->get<float>("zIndex"));
|
||||
else
|
||||
setZIndex(getDefaultZIndex());
|
||||
|
||||
if(properties & ThemeFlags::VISIBLE && elem->has("visible"))
|
||||
if (properties & ThemeFlags::VISIBLE && elem->has("visible"))
|
||||
setVisible(elem->get<bool>("visible"));
|
||||
else
|
||||
setVisible(true);
|
||||
|
@ -445,15 +448,14 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std
|
|||
|
||||
void GuiComponent::updateHelpPrompts()
|
||||
{
|
||||
if(getParent())
|
||||
{
|
||||
if (getParent()) {
|
||||
getParent()->updateHelpPrompts();
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<HelpPrompt> prompts = getHelpPrompts();
|
||||
|
||||
if(mWindow->peekGui() == this)
|
||||
if (mWindow->peekGui() == this)
|
||||
mWindow->setHelpPrompts(prompts, getHelpStyle());
|
||||
}
|
||||
|
||||
|
@ -469,30 +471,30 @@ bool GuiComponent::isProcessing() const
|
|||
|
||||
void GuiComponent::onShow()
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->onShow();
|
||||
}
|
||||
|
||||
void GuiComponent::onHide()
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->onHide();
|
||||
}
|
||||
|
||||
void GuiComponent::onScreenSaverActivate()
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->onScreenSaverActivate();
|
||||
}
|
||||
|
||||
void GuiComponent::onScreenSaverDeactivate()
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->onScreenSaverDeactivate();
|
||||
}
|
||||
|
||||
void GuiComponent::topWindow(bool isTop)
|
||||
{
|
||||
for(unsigned int i = 0; i < getChildCount(); i++)
|
||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||
getChild(i)->topWindow(isTop);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
//
|
||||
// GuiComponent.h
|
||||
//
|
||||
// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_CORE_GUI_COMPONENT_H
|
||||
#define ES_CORE_GUI_COMPONENT_H
|
||||
|
@ -25,36 +31,47 @@ public:
|
|||
|
||||
virtual void textInput(const char* text);
|
||||
|
||||
//Called when input is received.
|
||||
//Return true if the input is consumed, false if it should continue to be passed to other children.
|
||||
// Called when input is received.
|
||||
// Return true if the input is consumed, false if
|
||||
// it should continue to be passed to other children.
|
||||
virtual bool input(InputConfig* config, Input input);
|
||||
|
||||
//Called when time passes. Default implementation calls updateSelf(deltaTime) and updateChildren(deltaTime) - so you should probably call GuiComponent::update(deltaTime) at some point (or at least updateSelf so animations work).
|
||||
// Called when time passes.
|
||||
// Default implementation calls updateSelf(deltaTime) and updateChildren(deltaTime).
|
||||
// So you should probably call GuiComponent::update(deltaTime) at some point (or at
|
||||
// least updateSelf so animations work).
|
||||
virtual void update(int deltaTime);
|
||||
|
||||
//Called when it's time to render. By default, just calls renderChildren(parentTrans * getTransform()).
|
||||
//You probably want to override this like so:
|
||||
//1. Calculate the new transform that your control will draw at with Transform4x4f t = parentTrans * getTransform().
|
||||
//2. Set the renderer to use that new transform as the model matrix - Renderer::setMatrix(t);
|
||||
//3. Draw your component.
|
||||
//4. Tell your children to render, based on your component's transform - renderChildren(t).
|
||||
// Called when it's time to render.
|
||||
// By default, just calls renderChildren(parentTrans * getTransform()).
|
||||
// You probably want to override this like so:
|
||||
// 1. Calculate the new transform that your control will draw at with
|
||||
// Transform4x4f t = parentTrans * getTransform().
|
||||
// 2. Set the renderer to use that new transform as the model matrix
|
||||
// Renderer::setMatrix(t);
|
||||
// 3. Draw your component.
|
||||
// 4. Tell your children to render, based on your component's transform - renderChildren(t).
|
||||
virtual void render(const Transform4x4f& parentTrans);
|
||||
|
||||
Vector3f getPosition() const;
|
||||
inline void setPosition(const Vector3f& offset) { setPosition(offset.x(), offset.y(), offset.z()); }
|
||||
inline void setPosition(const Vector3f& offset)
|
||||
{ setPosition(offset.x(), offset.y(), offset.z()); }
|
||||
void setPosition(float x, float y, float z = 0.0f);
|
||||
virtual void onPositionChanged() {};
|
||||
|
||||
//Sets the origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
// Sets the origin as a percentage of this image.
|
||||
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
Vector2f getOrigin() const;
|
||||
void setOrigin(float originX, float originY);
|
||||
inline void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); }
|
||||
virtual void onOriginChanged() {};
|
||||
|
||||
//Sets the rotation origin as a percentage of this image (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
// Sets the rotation origin as a percentage of this image.
|
||||
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center)
|
||||
Vector2f getRotationOrigin() const;
|
||||
void setRotationOrigin(float originX, float originY);
|
||||
inline void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); }
|
||||
inline void setRotationOrigin(Vector2f origin)
|
||||
{ setRotationOrigin(origin.x(), origin.y()); }
|
||||
|
||||
virtual Vector2f getSize() const;
|
||||
inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); }
|
||||
|
@ -92,15 +109,22 @@ public:
|
|||
unsigned int getChildCount() const;
|
||||
GuiComponent* getChild(unsigned int i) const;
|
||||
|
||||
// animation will be automatically deleted when it completes or is stopped.
|
||||
// Animation will be automatically deleted when it completes or is stopped.
|
||||
bool isAnimationPlaying(unsigned char slot) const;
|
||||
bool isAnimationReversed(unsigned char slot) const;
|
||||
int getAnimationTime(unsigned char slot) const;
|
||||
void setAnimation(Animation* animation, int delay = 0, std::function<void()> finishedCallback = nullptr, bool reverse = false, unsigned char slot = 0);
|
||||
void setAnimation(Animation* animation, int delay = 0,
|
||||
std::function<void()> finishedCallback = nullptr,
|
||||
bool reverse = false, unsigned char slot = 0);
|
||||
bool stopAnimation(unsigned char slot);
|
||||
bool cancelAnimation(unsigned char slot); // Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving things in their current state. Returns true if successful (an animation was in this slot).
|
||||
bool finishAnimation(unsigned char slot); // Calls update(1.f) and finishedCallback, then deletes the animation - basically skips to the end. Returns true if successful (an animation was in this slot).
|
||||
bool advanceAnimation(unsigned char slot, unsigned int time); // Returns true if successful (an animation was in this slot).
|
||||
// Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving
|
||||
// things in their current state. Returns true if successful (an animation was in this slot).
|
||||
bool cancelAnimation(unsigned char slot);
|
||||
// Calls update(1.f) and finishedCallback, then deletes the animation - basically skips
|
||||
// to the end. Returns true if successful (an animation was in this slot).
|
||||
bool finishAnimation(unsigned char slot);
|
||||
// Returns true if successful (an animation was in this slot).
|
||||
bool advanceAnimation(unsigned char slot, unsigned int time);
|
||||
void stopAllAnimations();
|
||||
void cancelAllAnimations();
|
||||
|
||||
|
@ -124,7 +148,8 @@ public:
|
|||
|
||||
// Default implementation just handles <pos> and <size> tags as normalized float pairs.
|
||||
// You probably want to keep this behavior for any derived classes as well as add your own.
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties);
|
||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||
const std::string& view, const std::string& element, unsigned int properties);
|
||||
|
||||
// Returns a list of help prompts.
|
||||
virtual std::vector<HelpPrompt> getHelpPrompts() { return std::vector<HelpPrompt>(); };
|
||||
|
@ -137,10 +162,12 @@ public:
|
|||
// Returns true if the component is busy doing background processing (e.g. HTTP downloads)
|
||||
bool isProcessing() const;
|
||||
|
||||
const static unsigned char MAX_ANIMATIONS = 4;
|
||||
|
||||
protected:
|
||||
void renderChildren(const Transform4x4f& transform) const;
|
||||
void updateSelf(int deltaTime); // updates animations
|
||||
void updateChildren(int deltaTime); // updates animations
|
||||
void updateSelf(int deltaTime); // Updates animations
|
||||
void updateChildren(int deltaTime); // Updates animations
|
||||
|
||||
unsigned char mOpacity;
|
||||
Window* mWindow;
|
||||
|
@ -162,11 +189,9 @@ protected:
|
|||
bool mIsProcessing;
|
||||
bool mVisible;
|
||||
|
||||
public:
|
||||
const static unsigned char MAX_ANIMATIONS = 4;
|
||||
|
||||
private:
|
||||
Transform4x4f mTransform; //Don't access this directly! Use getTransform()!
|
||||
// Don't access this directly! Use getTransform()!
|
||||
Transform4x4f mTransform;
|
||||
AnimationController* mAnimationMap[MAX_ANIMATIONS];
|
||||
};
|
||||
|
||||
|
|
|
@ -68,6 +68,7 @@ void Settings::setDefaults()
|
|||
mStringMap["StartupSystem"] = "";
|
||||
mBoolMap["DisableKidStartMenu"] = true;
|
||||
mStringMap["MediaDirectory"] = "";
|
||||
mStringMap["DefaultSortOrder"] = "";
|
||||
|
||||
mBoolMap["VSync"] = true;
|
||||
|
||||
|
@ -139,9 +140,9 @@ void Settings::setDefaults()
|
|||
mStringMap["CollectionSystemsAuto"] = "";
|
||||
mStringMap["CollectionSystemsCustom"] = "";
|
||||
mBoolMap["CollectionShowSystemInfo"] = true;
|
||||
mBoolMap["SortAllSystems"] = false;
|
||||
mBoolMap["UseCustomCollectionsSystem"] = true;
|
||||
|
||||
mBoolMap["FavFirstCustom"] = true;
|
||||
mBoolMap["FavoritesFirst"] = true;
|
||||
mBoolMap["LaunchstringOverride"] = true;
|
||||
|
||||
|
|
|
@ -1,48 +1,77 @@
|
|||
//
|
||||
// GuiComplexTextEditPopup.cpp
|
||||
//
|
||||
// Text edit popup with a title, two text strings, a text input box and buttons
|
||||
// to load the second text string and to clear the input field.
|
||||
// Intended for updating settings for configuration files and similar.
|
||||
//
|
||||
|
||||
#include "guis/GuiComplexTextEditPopup.h"
|
||||
|
||||
#include "components/ButtonComponent.h"
|
||||
#include "components/MenuComponent.h"
|
||||
#include "components/TextEditComponent.h"
|
||||
|
||||
GuiComplexTextEditPopup::GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2,
|
||||
const std::string& initValue, const std::function<void(const std::string&)>& okCallback, bool multiLine, const char* acceptBtnText)
|
||||
: GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)), mMultiLine(multiLine)
|
||||
GuiComplexTextEditPopup::GuiComplexTextEditPopup(
|
||||
Window* window,
|
||||
const std::string& title,
|
||||
const std::string& infoString1,
|
||||
const std::string& infoString2,
|
||||
const std::string& initValue,
|
||||
const std::function<void(const std::string&)>& okCallback,
|
||||
bool multiLine, const char* acceptBtnText)
|
||||
: GuiComponent(window),
|
||||
mBackground(window, ":/frame.png"),
|
||||
mGrid(window, Vector2i(1, 5)),
|
||||
mMultiLine(multiLine)
|
||||
{
|
||||
addChild(&mBackground);
|
||||
addChild(&mGrid);
|
||||
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
||||
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
||||
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
||||
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
|
||||
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
||||
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1,
|
||||
Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
||||
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2,
|
||||
Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
||||
|
||||
mText = std::make_shared<TextEditComponent>(mWindow);
|
||||
mText->setValue(initValue);
|
||||
|
||||
if(!multiLine)
|
||||
if (!multiLine)
|
||||
mText->setCursor(initValue.size());
|
||||
|
||||
std::vector< std::shared_ptr<ButtonComponent> > buttons;
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText, [this, okCallback] { okCallback(mText->getValue()); delete this; }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "LOAD", "load default string", [this, infoString2] { mText->setValue(infoString2); }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear string", [this] { mText->setValue(""); }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes", [this] { delete this; }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
|
||||
[this, okCallback] { okCallback(mText->getValue()); delete this; }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "LOAD", "load default string",
|
||||
[this, infoString2] { mText->setValue(infoString2); }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear string",
|
||||
[this] { mText->setValue(""); }));
|
||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
|
||||
[this] { delete this; }));
|
||||
|
||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||
|
||||
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
|
||||
mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true);
|
||||
mGrid.setEntry(mInfoString2, Vector2i(0, 2), false, true);
|
||||
mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1), GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
||||
mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1),
|
||||
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
||||
mGrid.setEntry(mButtonGrid, Vector2i(0, 4), true, false);
|
||||
mGrid.setRowHeightPerc(1, 0.15, true);
|
||||
|
||||
float textHeight = mText->getFont()->getHeight();
|
||||
if(multiLine)
|
||||
|
||||
if (multiLine)
|
||||
textHeight *= 6;
|
||||
|
||||
mText->setSize(0, textHeight);
|
||||
|
||||
setSize(Renderer::getScreenWidth() * 0.75f, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + 220);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||
setSize(Renderer::getScreenWidth() * 0.75f,mTitle->getFont()->getHeight() +
|
||||
textHeight + mButtonGrid->getSize().y() + 220);
|
||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
||||
(Renderer::getScreenHeight() - mSize.y()) / 2);
|
||||
}
|
||||
|
||||
void GuiComplexTextEditPopup::onSizeChanged()
|
||||
|
@ -51,7 +80,7 @@ void GuiComplexTextEditPopup::onSizeChanged()
|
|||
|
||||
mText->setSize(mSize.x() - 40, mText->getSize().y());
|
||||
|
||||
// update grid
|
||||
// Update grid
|
||||
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
||||
mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y() / mSize.y());
|
||||
mGrid.setSize(mSize);
|
||||
|
@ -59,12 +88,11 @@ void GuiComplexTextEditPopup::onSizeChanged()
|
|||
|
||||
bool GuiComplexTextEditPopup::input(InputConfig* config, Input input)
|
||||
{
|
||||
if(GuiComponent::input(config, input))
|
||||
if (GuiComponent::input(config, input))
|
||||
return true;
|
||||
|
||||
// pressing back when not text editing closes us
|
||||
if(config->isMappedTo("b", input) && input.value)
|
||||
{
|
||||
// Pressing back button when not text editing closes us
|
||||
if (config->isMappedTo("b", input) && input.value) {
|
||||
delete this;
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
//
|
||||
// GuiComplexTextEditPopup.h
|
||||
//
|
||||
// Text edit popup with a title, two text strings, a text input box and buttons
|
||||
// to load the second text string and to clear the input field.
|
||||
// Intended for updating settings for configuration files and similar.
|
||||
//
|
||||
|
||||
#pragma once
|
||||
#ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H
|
||||
#define ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H
|
||||
|
@ -12,8 +20,15 @@ class TextEditComponent;
|
|||
class GuiComplexTextEditPopup : public GuiComponent
|
||||
{
|
||||
public:
|
||||
GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2,
|
||||
const std::string& initValue, const std::function<void(const std::string&)>& okCallback, bool multiLine, const char* acceptBtnText = "OK");
|
||||
GuiComplexTextEditPopup(
|
||||
Window* window,
|
||||
const std::string& title,
|
||||
const std::string& infoString1,
|
||||
const std::string& infoString2,
|
||||
const std::string& initValue,
|
||||
const std::function<void(const std::string&)>& okCallback,
|
||||
bool multiLine,
|
||||
const char* acceptBtnText = "OK");
|
||||
|
||||
bool input(InputConfig* config, Input input);
|
||||
void onSizeChanged();
|
||||
|
|
Loading…
Reference in a new issue