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:
Leon Styhre 2020-05-24 10:29:29 +02:00
parent f806285e06
commit f2f7d34bb6
35 changed files with 2288 additions and 1825 deletions

File diff suppressed because it is too large Load diff

View file

@ -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 #pragma once
#ifndef ES_APP_COLLECTION_SYSTEM_MANAGER_H #ifndef ES_APP_COLLECTION_SYSTEM_MANAGER_H
#define ES_APP_COLLECTION_SYSTEM_MANAGER_H #define ES_APP_COLLECTION_SYSTEM_MANAGER_H
@ -11,26 +30,22 @@ class SystemData;
class Window; class Window;
struct SystemEnvironmentData; struct SystemEnvironmentData;
enum CollectionSystemType enum CollectionSystemType {
{
AUTO_ALL_GAMES, AUTO_ALL_GAMES,
AUTO_LAST_PLAYED, AUTO_LAST_PLAYED,
AUTO_FAVORITES, AUTO_FAVORITES,
CUSTOM_COLLECTION CUSTOM_COLLECTION
}; };
struct CollectionSystemDecl struct CollectionSystemDecl {
{ CollectionSystemType type;
CollectionSystemType type; // type of system
std::string name; std::string name;
std::string longName; std::string longName;
std::string defaultSort;
std::string themeFolder; std::string themeFolder;
bool isCustom; bool isCustom;
}; };
struct CollectionSystemData struct CollectionSystemData {
{
SystemData* system; SystemData* system;
CollectionSystemDecl decl; CollectionSystemDecl decl;
bool isEnabled; bool isEnabled;
@ -57,8 +72,10 @@ public:
void updateCollectionSystem(FileData* file, CollectionSystemData sysData); void updateCollectionSystem(FileData* file, CollectionSystemData sysData);
void deleteCollectionFiles(FileData* file); void deleteCollectionFiles(FileData* file);
inline std::map<std::string, CollectionSystemData> getAutoCollectionSystems() { return mAutoCollectionSystemsData; }; inline std::map<std::string, CollectionSystemData> getAutoCollectionSystems()
inline std::map<std::string, CollectionSystemData> getCustomCollectionSystems() { return mCustomCollectionSystemsData; }; { return mAutoCollectionSystemsData; };
inline std::map<std::string, CollectionSystemData> getCustomCollectionSystems()
{ return mCustomCollectionSystemsData; };
inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; }; inline SystemData* getCustomCollectionsBundle() { return mCustomCollectionsBundle; };
std::vector<std::string> getUnusedSystemsFromTheme(); std::vector<std::string> getUnusedSystemsFromTheme();
SystemData* addNewCustomCollection(std::string name); SystemData* addNewCustomCollection(std::string name);
@ -76,6 +93,8 @@ public:
SystemData* getSystemToView(SystemData* sys); SystemData* getSystemToView(SystemData* sys);
void updateCollectionFolderMetadata(SystemData* sys); void updateCollectionFolderMetadata(SystemData* sys);
bool getIsCustomCollection(SystemData* system);
private: private:
static CollectionSystemManager* sInstance; static CollectionSystemManager* sInstance;
SystemEnvironmentData* mCollectionEnvData; SystemEnvironmentData* mCollectionEnvData;
@ -90,12 +109,14 @@ private:
void initAutoCollectionSystems(); void initAutoCollectionSystems();
void initCustomCollectionSystems(); void initCustomCollectionSystems();
SystemData* getAllGamesCollection(); 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 populateAutoCollection(CollectionSystemData* sysData);
void populateCustomCollection(CollectionSystemData* sysData); void populateCustomCollection(CollectionSystemData* sysData);
void removeCollectionsFromDisplayedSystems(); 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> getSystemsFromConfig();
std::vector<std::string> getSystemsFromTheme(); std::vector<std::string> getSystemsFromTheme();

View file

@ -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 "FileData.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
@ -16,11 +24,22 @@
#include "Window.h" #include "Window.h"
#include <assert.h> #include <assert.h>
FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData* envData, SystemData* system) FileData::FileData(
: 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! 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) // Metadata needs at least a name field (since that's what getName() will return).
if(metadata.get("name").empty()) if (metadata.get("name").empty())
metadata.set("name", getDisplayName()); metadata.set("name", getDisplayName());
mSystemName = system->getName(); mSystemName = system->getName();
metadata.resetChangedFlag(); metadata.resetChangedFlag();
@ -28,10 +47,10 @@ FileData::FileData(FileType type, const std::string& path, SystemEnvironmentData
FileData::~FileData() FileData::~FileData()
{ {
if(mParent) if (mParent)
mParent->removeChild(this); mParent->removeChild(this);
if(mType == GAME) if (mType == GAME)
mSystem->getIndex()->removeFromIndex(this); mSystem->getIndex()->removeFromIndex(this);
mChildren.clear(); mChildren.clear();
@ -40,9 +59,6 @@ FileData::~FileData()
std::string FileData::getDisplayName() const std::string FileData::getDisplayName() const
{ {
std::string stem = Utils::FileSystem::getStem(mPath); std::string stem = Utils::FileSystem::getStem(mPath);
// if(mSystem && mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))
// stem = MameNames::getInstance()->getRealName(stem);
return stem; return stem;
} }
@ -77,25 +93,19 @@ const std::string FileData::getMediaDirectory() const
std::string mediaDirSetting = Settings::getInstance()->getString("MediaDirectory"); std::string mediaDirSetting = Settings::getInstance()->getString("MediaDirectory");
std::string mediaDirPath = ""; std::string mediaDirPath = "";
if(mediaDirSetting == "") if (mediaDirSetting == "") {
{
mediaDirPath = Utils::FileSystem::getHomePath() + "/.emulationstation/downloaded_media/"; mediaDirPath = Utils::FileSystem::getHomePath() + "/.emulationstation/downloaded_media/";
} }
else else {
{
mediaDirPath = mediaDirSetting; mediaDirPath = mediaDirSetting;
// Expand home symbol if the path starts with ~ // Expand home symbol if the path starts with ~
if(mediaDirPath[0] == '~') if (mediaDirPath[0] == '~') {
{
mediaDirPath.erase(0, 1); mediaDirPath.erase(0, 1);
mediaDirPath.insert(0, Utils::FileSystem::getHomePath()); mediaDirPath.insert(0, Utils::FileSystem::getHomePath());
} }
if (mediaDirPath.back() != '/')
if(mediaDirPath.back() != '/')
{
mediaDirPath = mediaDirPath + "/"; mediaDirPath = mediaDirPath + "/";
}
} }
return mediaDirPath; return mediaDirPath;
@ -106,21 +116,21 @@ const std::string FileData::getThumbnailPath() const
const char* extList[2] = { ".png", ".jpg" }; const char* extList[2] = { ".png", ".jpg" };
std::string tempPath = getMediaDirectory() + mSystemName + "/thumbnails/" + getDisplayName(); std::string tempPath = getMediaDirectory() + mSystemName + "/thumbnails/" + getDisplayName();
// Look for media in the media directory // Look for media in the media directory.
for(int i = 0; i < 2; i++) for (int i = 0; i < 2; i++) {
{
std::string mediaPath = tempPath + extList[i]; std::string mediaPath = tempPath + extList[i];
if(Utils::FileSystem::exists(mediaPath)) if (Utils::FileSystem::exists(mediaPath))
return mediaPath; return mediaPath;
} }
// No media found in the media directory, so look for local art as well (if configured to do so) // No media found in the media directory, so look
if(Settings::getInstance()->getBool("LocalArt")) // for local art as well (if configured to do so).
if (Settings::getInstance()->getBool("LocalArt"))
{ {
for(int i = 0; i < 2; i++) for (int i = 0; i < 2; i++) {
{ std::string localMediaPath = mEnvData->mStartPath + "/images/" +
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-thumbnail" + extList[i]; getDisplayName() + "-thumbnail" + extList[i];
if(Utils::FileSystem::exists(localMediaPath)) if (Utils::FileSystem::exists(localMediaPath))
return localMediaPath; return localMediaPath;
} }
} }
@ -133,21 +143,21 @@ const std::string FileData::getVideoPath() const
const char* extList[5] = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" }; const char* extList[5] = { ".avi", ".mkv", ".mov", ".mp4", ".wmv" };
std::string tempPath = getMediaDirectory() + mSystemName + "/videos/" + getDisplayName(); std::string tempPath = getMediaDirectory() + mSystemName + "/videos/" + getDisplayName();
// Look for media in the media directory // Look for media in the media directory.
for(int i = 0; i < 5; i++) for (int i = 0; i < 5; i++) {
{
std::string mediaPath = tempPath + extList[i]; std::string mediaPath = tempPath + extList[i];
if(Utils::FileSystem::exists(mediaPath)) if (Utils::FileSystem::exists(mediaPath))
return mediaPath; return mediaPath;
} }
// No media found in the media directory, so look for local art as well (if configured to do so) // No media found in the media directory, so look
if(Settings::getInstance()->getBool("LocalArt")) // for local art as well (if configured to do so).
if (Settings::getInstance()->getBool("LocalArt"))
{ {
for(int i = 0; i < 5; i++) for (int i = 0; i < 5; i++) {
{ std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() +
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-video" + extList[i]; "-video" + extList[i];
if(Utils::FileSystem::exists(localMediaPath)) if (Utils::FileSystem::exists(localMediaPath))
return localMediaPath; return localMediaPath;
} }
} }
@ -160,21 +170,21 @@ const std::string FileData::getMarqueePath() const
const char* extList[2] = { ".png", ".jpg" }; const char* extList[2] = { ".png", ".jpg" };
std::string tempPath = getMediaDirectory() + mSystemName + "/marquees/" + getDisplayName(); std::string tempPath = getMediaDirectory() + mSystemName + "/marquees/" + getDisplayName();
// Look for media in the media directory // Look for media in the media directory.
for(int i = 0; i < 2; i++) for (int i = 0; i < 2; i++) {
{
std::string mediaPath = tempPath + extList[i]; std::string mediaPath = tempPath + extList[i];
if(Utils::FileSystem::exists(mediaPath)) if (Utils::FileSystem::exists(mediaPath))
return mediaPath; return mediaPath;
} }
// No media found in the media directory, so look for local art as well (if configured to do so) // No media found in the media directory, so look
if(Settings::getInstance()->getBool("LocalArt")) // for local art as well (if configured to do so).
if (Settings::getInstance()->getBool("LocalArt"))
{ {
for(int i = 0; i < 2; i++) for (int i = 0; i < 2; i++) {
{ std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() +
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-marquee" + extList[i]; "-marquee" + extList[i];
if(Utils::FileSystem::exists(localMediaPath)) if (Utils::FileSystem::exists(localMediaPath))
return localMediaPath; return localMediaPath;
} }
} }
@ -186,32 +196,30 @@ const std::string FileData::getImagePath() const
{ {
const char* extList[2] = { ".png", ".jpg" }; 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(); 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]; std::string mediaPath = tempPath + extList[i];
if(Utils::FileSystem::exists(mediaPath)) if (Utils::FileSystem::exists(mediaPath))
return mediaPath; return mediaPath;
} }
// If no mix image exists, try normal screenshot // If no mix image exists, try normal screenshot.
tempPath = getMediaDirectory() + mSystemName + "/screenshots/" + getDisplayName(); 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]; std::string mediaPath = tempPath + extList[i];
if(Utils::FileSystem::exists(mediaPath)) if (Utils::FileSystem::exists(mediaPath))
return mediaPath; return mediaPath;
} }
// No media found in the media directory, so look for local art as well (if configured to do so) // No media found in the media directory, so look
if(Settings::getInstance()->getBool("LocalArt")) // for local art as well (if configured to do so).
{ if (Settings::getInstance()->getBool("LocalArt")) {
for(int i = 0; i < 2; i++) for (int i = 0; i < 2; i++) {
{ std::string localMediaPath = mEnvData->mStartPath + "/images/" +
std::string localMediaPath = mEnvData->mStartPath + "/images/" + getDisplayName() + "-image" + extList[i]; getDisplayName() + "-image" + extList[i];
if(Utils::FileSystem::exists(localMediaPath)) if (Utils::FileSystem::exists(localMediaPath))
return localMediaPath; return localMediaPath;
} }
} }
@ -225,17 +233,14 @@ const std::vector<FileData*>& FileData::getChildrenListToDisplay()
FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex(); FileFilterIndex* idx = CollectionSystemManager::get()->getSystemToView(mSystem)->getIndex();
if (idx->isFiltered()) { if (idx->isFiltered()) {
mFilteredChildren.clear(); mFilteredChildren.clear();
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
{
if (idx->showFile((*it))) { if (idx->showFile((*it))) {
mFilteredChildren.push_back(*it); mFilteredChildren.push_back(*it);
} }
} }
return mFilteredChildren; return mFilteredChildren;
} }
else else {
{
return mChildren; return mChildren;
} }
} }
@ -245,16 +250,12 @@ std::vector<FileData*> FileData::getFilesRecursive(unsigned int typeMask, bool d
std::vector<FileData*> out; std::vector<FileData*> out;
FileFilterIndex* idx = mSystem->getIndex(); FileFilterIndex* idx = mSystem->getIndex();
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
{ if ((*it)->getType() & typeMask) {
if((*it)->getType() & typeMask)
{
if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it)) if (!displayedOnly || !idx->isFiltered() || idx->showFile(*it))
out.push_back(*it); out.push_back(*it);
} }
if ((*it)->getChildren().size() > 0) {
if((*it)->getChildren().size() > 0)
{
std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly); std::vector<FileData*> subchildren = (*it)->getFilesRecursive(typeMask, displayedOnly);
out.insert(out.cend(), subchildren.cbegin(), subchildren.cend()); out.insert(out.cend(), subchildren.cbegin(), subchildren.cend());
} }
@ -271,9 +272,10 @@ const bool FileData::isArcadeAsset()
{ {
const std::string stem = Utils::FileSystem::getStem(mPath); const std::string stem = Utils::FileSystem::getStem(mPath);
return ( return (
(mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) || mSystem->hasPlatformId(PlatformIds::NEOGEO))) (mSystem && (mSystem->hasPlatformId(PlatformIds::ARCADE) ||
&& mSystem->hasPlatformId(PlatformIds::NEOGEO))) &&
(MameNames::getInstance()->isBios(stem) || MameNames::getInstance()->isDevice(stem)) (MameNames::getInstance()->isBios(stem) ||
MameNames::getInstance()->isDevice(stem))
); );
} }
@ -288,8 +290,7 @@ void FileData::addChild(FileData* file)
assert(file->getParent() == NULL); assert(file->getParent() == NULL);
const std::string key = file->getKey(); const std::string key = file->getKey();
if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) if (mChildrenByFilename.find(key) == mChildrenByFilename.cend()) {
{
mChildrenByFilename[key] = file; mChildrenByFilename[key] = file;
mChildren.push_back(file); mChildren.push_back(file);
file->mParent = this; file->mParent = this;
@ -301,10 +302,8 @@ void FileData::removeChild(FileData* file)
assert(mType == FOLDER); assert(mType == FOLDER);
assert(file->getParent() == this); assert(file->getParent() == this);
mChildrenByFilename.erase(file->getKey()); mChildrenByFilename.erase(file->getKey());
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
{ if (*it == file) {
if(*it == file)
{
file->mParent = NULL; file->mParent = NULL;
mChildren.erase(it); mChildren.erase(it);
return; return;
@ -320,19 +319,59 @@ void FileData::sort(ComparisonFunction& comparator, bool ascending)
{ {
std::stable_sort(mChildren.begin(), mChildren.end(), comparator); std::stable_sort(mChildren.begin(), mChildren.end(), comparator);
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++) {
{ if ((*it)->getChildren().size() > 0)
if((*it)->getChildren().size() > 0)
(*it)->sort(comparator, ascending); (*it)->sort(comparator, ascending);
} }
if(!ascending) if (!ascending)
std::reverse(mChildren.begin(), mChildren.end()); 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) void FileData::launchGame(Window* window)
@ -346,15 +385,13 @@ void FileData::launchGame(Window* window)
std::string command = ""; std::string command = "";
// Check if there is a launch string override for the game and the corresponding option has been set // Check if there is a launch string override for the game
if(Settings::getInstance()->getBool("LaunchstringOverride") && !metadata.get("launchstring").empty()) // and the corresponding option to use it has been set.
{ if (Settings::getInstance()->getBool("LaunchstringOverride") &&
!metadata.get("launchstring").empty())
command = metadata.get("launchstring"); command = metadata.get("launchstring");
}
else else
{
command = mEnvData->mLaunchCommand; command = mEnvData->mLaunchCommand;
}
const std::string rom = Utils::FileSystem::getEscapedPath(getPath()); const std::string rom = Utils::FileSystem::getEscapedPath(getPath());
const std::string basename = Utils::FileSystem::getStem(getPath()); const std::string basename = Utils::FileSystem::getStem(getPath());
@ -369,25 +406,23 @@ void FileData::launchGame(Window* window)
LOG(LogInfo) << " " << command; LOG(LogInfo) << " " << command;
int exitCode = runSystemCommand(command); int exitCode = runSystemCommand(command);
if(exitCode != 0) if (exitCode != 0)
{
LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!"; LOG(LogWarning) << "...launch terminated with nonzero exit code " << exitCode << "!";
}
Scripting::fireEvent("game-end"); Scripting::fireEvent("game-end");
// window->init(); // window->init();
VolumeControl::getInstance()->init(); VolumeControl::getInstance()->init();
window->normalizeNextUpdate(); window->normalizeNextUpdate();
//update number of times the game has been launched // Update number of times the game has been launched.
FileData* gameToUpdate = getSourceFileData(); FileData* gameToUpdate = getSourceFileData();
int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1; int timesPlayed = gameToUpdate->metadata.getInt("playcount") + 1;
gameToUpdate->metadata.set("playcount", std::to_string(static_cast<long long>(timesPlayed))); 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())); gameToUpdate->metadata.set("lastplayed", Utils::Time::DateTime(Utils::Time::now()));
CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate); CollectionSystemManager::get()->refreshCollectionSystems(gameToUpdate);
@ -395,9 +430,10 @@ void FileData::launchGame(Window* window)
} }
CollectionFileData::CollectionFileData(FileData* file, SystemData* system) 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(); mSourceFileData = file->getSourceFileData();
refreshMetadata(); refreshMetadata();
mParent = NULL; mParent = NULL;
@ -407,8 +443,8 @@ CollectionFileData::CollectionFileData(FileData* file, SystemData* system)
CollectionFileData::~CollectionFileData() CollectionFileData::~CollectionFileData()
{ {
// need to remove collection file data at the collection object destructor // Need to remove collection file data at the collection object destructor.
if(mParent) if (mParent)
mParent->removeChild(this); mParent->removeChild(this);
mParent = NULL; mParent = NULL;
} }
@ -431,28 +467,28 @@ void CollectionFileData::refreshMetadata()
const std::string& CollectionFileData::getName() const std::string& CollectionFileData::getName()
{ {
if (mDirty) { if (mDirty) {
mCollectionFileName = Utils::String::removeParenthesis(mSourceFileData->metadata.get("name")); mCollectionFileName =
mCollectionFileName += " [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]"; Utils::String::removeParenthesis(mSourceFileData->metadata.get("name"));
mCollectionFileName +=
" [" + Utils::String::toUpper(mSourceFileData->getSystem()->getName()) + "]";
mDirty = false; mDirty = false;
} }
if (Settings::getInstance()->getBool("CollectionShowSystemInfo")) if (Settings::getInstance()->getBool("CollectionShowSystemInfo"))
return mCollectionFileName; return mCollectionFileName;
return mSourceFileData->metadata.get("name"); 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) { FileData::SortType getSortTypeFromString(std::string desc) {
std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes; std::vector<FileData::SortType> SortTypes = FileSorts::SortTypes;
// find it // Find it
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) for (unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) {
{
const FileData::SortType& sort = FileSorts::SortTypes.at(i); const FileData::SortType& sort = FileSorts::SortTypes.at(i);
if(sort.description == desc) if (sort.description == desc)
{
return sort; return sort;
}
} }
// if not found default to name, ascending // If no type found then default to "filename, ascending".
return FileSorts::SortTypes.at(0); return FileSorts::SortTypes.at(0);
} }

View file

@ -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 #pragma once
#ifndef ES_APP_FILE_DATA_H #ifndef ES_APP_FILE_DATA_H
#define ES_APP_FILE_DATA_H #define ES_APP_FILE_DATA_H
@ -10,15 +18,13 @@ class SystemData;
class Window; class Window;
struct SystemEnvironmentData; struct SystemEnvironmentData;
enum FileType enum FileType {
{
GAME = 1, // Cannot have children. GAME = 1, // Cannot have children.
FOLDER = 2, FOLDER = 2,
PLACEHOLDER = 3 PLACEHOLDER = 3
}; };
enum FileChangeType enum FileChangeType {
{
FILE_ADDED, FILE_ADDED,
FILE_METADATA_CHANGED, FILE_METADATA_CHANGED,
FILE_REMOVED, FILE_REMOVED,
@ -33,7 +39,11 @@ FileType stringToFileType(const char* str);
class FileData class FileData
{ {
public: 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 ~FileData();
virtual const std::string& getName(); virtual const std::string& getName();
@ -42,7 +52,8 @@ public:
inline FileType getType() const { return mType; } inline FileType getType() const { return mType; }
inline const std::string& getPath() const { return mPath; } inline const std::string& getPath() const { return mPath; }
inline FileData* getParent() const { return mParent; } 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 const std::vector<FileData*>& getChildren() const { return mChildren; }
inline SystemData* getSystem() const { return mSystem; } inline SystemData* getSystem() const { return mSystem; }
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
@ -53,7 +64,8 @@ public:
virtual const std::string getImagePath() const; virtual const std::string getImagePath() const;
const std::vector<FileData*>& getChildrenListToDisplay(); 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 addChild(FileData* file); // Error if mType != FOLDER
void removeChild(FileData* file); //Error if mType != FOLDER void removeChild(FileData* file); //Error if mType != FOLDER
@ -69,33 +81,42 @@ public:
virtual FileData* getSourceFileData(); virtual FileData* getSourceFileData();
inline std::string getSystemName() const { return mSystemName; }; 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; std::string getDisplayName() const;
// As above, but also remove parenthesis // As above, but also remove parenthesis.
std::string getCleanName() const; std::string getCleanName() const;
void launchGame(Window* window); void launchGame(Window* window);
typedef bool ComparisonFunction(const FileData* a, const FileData* b); typedef bool ComparisonFunction(const FileData* a, const FileData* b);
struct SortType struct SortType {
{
ComparisonFunction* comparisonFunction; ComparisonFunction* comparisonFunction;
bool ascending; bool ascending;
std::string description; std::string description;
SortType(ComparisonFunction* sortFunction, bool sortAscending, const std::string & sortDescription) SortType(ComparisonFunction* sortFunction,
: comparisonFunction(sortFunction), ascending(sortAscending), description(sortDescription) {} bool sortAscending,
const std::string& sortDescription)
: comparisonFunction(sortFunction),
ascending(sortAscending),
description(sortDescription) {}
}; };
void sort(ComparisonFunction& comparator, bool ascending = true); 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; MetaDataList metadata;
inline void setSortTypeString(std::string typestring) { mSortTypeString = typestring; }
inline std::string getSortTypeString() { return mSortTypeString; }
protected: protected:
FileData* mSourceFileData; FileData* mSourceFileData;
FileData* mParent; FileData* mParent;
std::string mSystemName; std::string mSystemName;
std::string mSortTypeString = "";
private: private:
FileType mType; FileType mType;
@ -117,7 +138,7 @@ public:
FileData* getSourceFileData(); FileData* getSourceFileData();
std::string getKey(); std::string getKey();
private: private:
// needs to be updated when metadata changes // Needs to be updated when metadata changes.
std::string mCollectionFileName; std::string mCollectionFileName;
bool mDirty; bool mDirty;
}; };

View file

@ -1,3 +1,9 @@
//
// Gamelist.cpp
//
// Parses and updates the gamelist.xml files.
//
#include "Gamelist.h" #include "Gamelist.h"
#include <chrono> #include <chrono>
@ -12,24 +18,24 @@
FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType type) 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(); FileData* root = system->getRootFolder();
bool contains = false; bool contains = false;
std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains); std::string relative = Utils::FileSystem::removeCommonPath(path, root->getPath(), contains);
if(!contains) if (!contains) {
{ LOG(LogError) << "File path \"" << path << "\" is outside system path \"" <<
LOG(LogError) << "File path \"" << path << "\" is outside system path \"" << system->getStartPath() << "\""; system->getStartPath() << "\"";
return NULL; return nullptr;
} }
Utils::FileSystem::stringList pathList = Utils::FileSystem::getPathList(relative); Utils::FileSystem::stringList pathList = Utils::FileSystem::getPathList(relative);
auto path_it = pathList.begin(); auto path_it = pathList.begin();
FileData* treeNode = root; FileData* treeNode = root;
bool found = false; bool found = false;
while(path_it != pathList.end()) while (path_it != pathList.end()) {
{ const std::unordered_map<std::string, FileData*>& children =
const std::unordered_map<std::string, FileData*>& children = treeNode->getChildrenByFilename(); treeNode->getChildrenByFilename();
std::string key = *path_it; std::string key = *path_it;
found = children.find(key) != children.cend(); found = children.find(key) != children.cend();
@ -37,40 +43,35 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
treeNode = children.at(key); treeNode = children.at(key);
} }
// this is the end // This is the end
if(path_it == --pathList.end()) if (path_it == --pathList.end()) {
{ if (found)
if(found)
return treeNode; return treeNode;
if(type == FOLDER) if (type == FOLDER) {
{ LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create";
LOG(LogWarning) << "gameList: folder doesn't already exist, won't create"; return nullptr;
return NULL;
} }
FileData* file = new FileData(type, path, system->getSystemEnvData(), system); FileData* file = new FileData(type, path, system->getSystemEnvData(), system);
// skipping arcade assets from gamelist // Skipping arcade assets from gamelist.
if(!file->isArcadeAsset()) if (!file->isArcadeAsset())
{
treeNode->addChild(file); treeNode->addChild(file);
}
return file; return file;
} }
if(!found) if (!found) {
{ // Don't create folders unless they're including any games.
// don't create folders unless it's leading up to a game // If the type is FOLDER it's going to be empty, so don't bother.
// if type is a folder it's gonna be empty, so don't bother if (type == FOLDER) {
if(type == FOLDER) LOG(LogWarning) << "Gamelist: folder doesn't exist, won't create";
{ return nullptr;
LOG(LogWarning) << "gameList: folder doesn't already exist, won't create";
return NULL;
} }
// create missing folder // Create missing folder.
FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath()) + "/" + *path_it, system->getSystemEnvData(), system); FileData* folder = new FileData(FOLDER, Utils::FileSystem::getStem(treeNode->getPath())
+ "/" + *path_it, system->getSystemEnvData(), system);
treeNode->addChild(folder); treeNode->addChild(folder);
treeNode = folder; treeNode = folder;
} }
@ -78,7 +79,7 @@ FileData* findOrCreateFile(SystemData* system, const std::string& path, FileType
path_it++; path_it++;
} }
return NULL; return nullptr;
} }
void parseGamelist(SystemData* system) void parseGamelist(SystemData* system)
@ -86,7 +87,7 @@ void parseGamelist(SystemData* system)
bool trustGamelist = Settings::getInstance()->getBool("ParseGamelistOnly"); bool trustGamelist = Settings::getInstance()->getBool("ParseGamelistOnly");
std::string xmlpath = system->getGamelistPath(false); std::string xmlpath = system->getGamelistPath(false);
if(!Utils::FileSystem::exists(xmlpath)) if (!Utils::FileSystem::exists(xmlpath))
return; return;
LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\"..."; LOG(LogInfo) << "Parsing XML file \"" << xmlpath << "\"...";
@ -94,15 +95,14 @@ void parseGamelist(SystemData* system)
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str()); pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
if(!result) if (!result) {
{ LOG(LogError) << "Error parsing XML file \"" << xmlpath <<
LOG(LogError) << "Error parsing XML file \"" << xmlpath << "\"!\n " << result.description(); "\"!\n " <<result.description();
return; return;
} }
pugi::xml_node root = doc.child("gameList"); pugi::xml_node root = doc.child("gameList");
if(!root) if (!root) {
{
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!"; LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlpath << "\"!";
return; return;
} }
@ -111,33 +111,32 @@ void parseGamelist(SystemData* system)
const char* tagList[2] = { "game", "folder" }; const char* tagList[2] = { "game", "folder" };
FileType typeList[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]; const char* tag = tagList[i];
FileType type = typeList[i]; FileType type = typeList[i];
for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag)) 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); 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."; LOG(LogWarning) << "File \"" << path << "\" does not exist! Ignoring.";
continue; continue;
} }
FileData* file = findOrCreateFile(system, path, type); FileData* file = findOrCreateFile(system, path, type);
if(!file) if (!file) {
{ LOG(LogError) << "Error finding/creating FileData for \"" <<
LOG(LogError) << "Error finding/creating FileData for \"" << path << "\", skipping."; path << "\", skipping.";
continue; continue;
} }
else if(!file->isArcadeAsset()) else if (!file->isArcadeAsset()) {
{
std::string defaultName = file->metadata.get("name"); std::string defaultName = file->metadata.get("name");
file->metadata = MetaDataList::createFromXML(GAME_METADATA, fileNode, relativeTo); file->metadata = MetaDataList::createFromXML(GAME_METADATA, fileNode, relativeTo);
//make sure name gets set if one didn't exist // Make sure a name gets set if one doesn't exist.
if(file->metadata.get("name").empty()) if (file->metadata.get("name").empty())
file->metadata.set("name", defaultName); file->metadata.set("name", defaultName);
file->metadata.resetChangedFlag(); 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); pugi::xml_node newNode = parent.append_child(tag);
//write metadata // Write metadata.
file->metadata.appendToXML(newNode, true, system->getStartPath()); file->metadata.appendToXML(newNode, true, system->getStartPath());
if(newNode.children().begin() == newNode.child("name") //first element is name // First element is "name", there's only one element and the name is the default.
&& ++newNode.children().begin() == newNode.children().end() //theres only one element if (newNode.children().begin() == newNode.child("name") &&
&& newNode.child("name").text().get() == file->getDisplayName()) //the name is the default ++newNode.children().begin() == newNode.children().end() &&
{ newNode.child("name").text().get() == file->getDisplayName()) {
//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 // If the only info is the default name, don't bother
newNode.prepend_child("path").text().set(Utils::FileSystem::createRelativePath(file->getPath(), system->getStartPath(), false).c_str()); // 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) void updateGamelist(SystemData* system)
{ {
//We do this by reading the XML again, adding changes and then writing it back, // 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 would then miss in the new XML. // because there might be information missing in our systemdata which we would otherwise
//We have the complete information for every game though, so we can simply remove a game // miss in the new XML file. We have the complete information for every game though, so
//we already have in the system from the XML, and then add it back from its GameData information... // 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")) if (Settings::getInstance()->getBool("IgnoreGamelist"))
return; return;
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_node root; pugi::xml_node root;
std::string xmlReadPath = system->getGamelistPath(false); std::string xmlReadPath = system->getGamelistPath(false);
if(Utils::FileSystem::exists(xmlReadPath)) if (Utils::FileSystem::exists(xmlReadPath)) {
{ // Parse an existing file first.
//parse an existing file first
pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str()); pugi::xml_parse_result result = doc.load_file(xmlReadPath.c_str());
if(!result) if (!result) {
{ LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " <<
LOG(LogError) << "Error parsing XML file \"" << xmlReadPath << "\"!\n " << result.description(); result.description();
return; return;
} }
root = doc.child("gameList"); root = doc.child("gameList");
if(!root) if (!root) {
{ LOG(LogError) << "Could not find <gameList> node in gamelist \"" <<
LOG(LogError) << "Could not find <gameList> node in gamelist \"" << xmlReadPath << "\"!"; xmlReadPath << "\"!";
return; return;
} }
}else{ }
//set up an empty gamelist to append to else {
// Set up an empty gamelist to append to.
root = doc.append_child("gameList"); root = doc.append_child("gameList");
} }
// Now we have all the information from the XML file, so iterate
//now we have all the information from the XML. now iterate through all our games and add information from there // through all our games and add the information from there.
FileData* rootFolder = system->getRootFolder(); FileData* rootFolder = system->getRootFolder();
if (rootFolder != nullptr) if (rootFolder != nullptr) {
{
int numUpdated = 0; int numUpdated = 0;
//get only files, no folders // Get only files, no folders.
std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER); std::vector<FileData*> files = rootFolder->getFilesRecursive(GAME | FOLDER);
//iterate through all files, checking if they're already in the XML // 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) for (std::vector<FileData*>::const_iterator fit = files.cbegin();
{ fit != files.cend(); ++fit) {
const char* tag = ((*fit)->getType() == GAME) ? "game" : "folder"; 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()) if (!(*fit)->metadata.wasChanged())
continue; continue;
// check if the file already exists in the XML // Check if the file already exists in the XML file.
// if it does, remove it before adding // If it does, remove the entry before adding it back.
for(pugi::xml_node fileNode = root.child(tag); fileNode; fileNode = fileNode.next_sibling(tag)) for (pugi::xml_node fileNode = root.child(tag); fileNode;
{ fileNode = fileNode.next_sibling(tag)) {
pugi::xml_node pathNode = fileNode.child("path"); pugi::xml_node pathNode = fileNode.child("path");
if(!pathNode) if (!pathNode) {
{
LOG(LogError) << "<" << tag << "> node contains no <path> child!"; LOG(LogError) << "<" << tag << "> node contains no <path> child!";
continue; 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()); std::string gamePath = Utils::FileSystem::getCanonicalPath((*fit)->getPath());
if(nodePath == gamePath) if (nodePath == gamePath) {
{ // Found it
// found it
root.remove_child(fileNode); root.remove_child(fileNode);
break; 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); addFileDataNode(root, *fit, tag, system);
++numUpdated; ++numUpdated;
} }
//now write the file // Now write the file.
if (numUpdated > 0) { if (numUpdated > 0) {
const auto startTs = std::chrono::system_clock::now(); 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)); std::string xmlWritePath(system->getGamelistPath(true));
Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(xmlWritePath)); 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())) { 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(); 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() << "\"!"; LOG(LogError) << "Found no root folder for system \"" << system->getName() << "\"!";
} }
} }

View file

@ -1,3 +1,9 @@
//
// Gamelist.h
//
// Parses and updates the gamelist.xml files.
//
#pragma once #pragma once
#ifndef ES_APP_GAME_LIST_H #ifndef ES_APP_GAME_LIST_H
#define ES_APP_GAME_LIST_H #define ES_APP_GAME_LIST_H

View file

@ -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 "MetaData.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
@ -24,7 +31,8 @@ MetaDataDecl gameDecls[] = {
{"lastplayed", MD_TIME, "0", true, "last played", "enter last played date"} {"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[] = { MetaDataDecl folderDecls[] = {
{"name", MD_STRING, "", false, "name", "enter game name"}, {"name", MD_STRING, "", false, "name", "enter game name"},
@ -37,12 +45,12 @@ MetaDataDecl folderDecls[] = {
{"genre", MD_STRING, "unknown", false, "genre", "enter game genre"}, {"genre", MD_STRING, "unknown", false, "genre", "enter game genre"},
{"players", MD_INT, "1", false, "players", "enter number of players"} {"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) const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
{ {
switch(type) switch(type) {
{
case GAME_METADATA: case GAME_METADATA:
return gameMDD; return gameMDD;
case FOLDER_METADATA: case FOLDER_METADATA:
@ -53,36 +61,31 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type)
return gameMDD; return gameMDD;
} }
MetaDataList::MetaDataList(MetaDataListType type) MetaDataList::MetaDataList(MetaDataListType type)
: mType(type), mWasChanged(false) : mType(type), mWasChanged(false)
{ {
const std::vector<MetaDataDecl>& mdd = getMDD(); 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); set(iter->key, iter->defaultValue);
} }
MetaDataList MetaDataList::createFromXML(MetaDataListType type,
MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo) pugi::xml_node& node, const std::string& relativeTo)
{ {
MetaDataList mdl(type); MetaDataList mdl(type);
const std::vector<MetaDataDecl>& mdd = mdl.getMDD(); 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()); pugi::xml_node md = node.child(iter->key.c_str());
if(md) if (md) {
{ // If it's a path, resolve relative paths.
// if it's a path, resolve relative paths
std::string value = md.text().get(); std::string value = md.text().get();
if (iter->type == MD_PATH) if (iter->type == MD_PATH)
{
value = Utils::FileSystem::resolveRelativePath(value, relativeTo, true); value = Utils::FileSystem::resolveRelativePath(value, relativeTo, true);
}
mdl.set(iter->key, value); mdl.set(iter->key, value);
}else{ }
else {
mdl.set(iter->key, iter->defaultValue); mdl.set(iter->key, iter->defaultValue);
} }
} }
@ -90,21 +93,20 @@ MetaDataList MetaDataList::createFromXML(MetaDataListType type, pugi::xml_node&
return mdl; 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(); 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); auto mapIter = mMap.find(mddIter->key);
if(mapIter != mMap.cend()) if (mapIter != mMap.cend()) {
{ // We have this value!
// we have this value! // If it's just the default (and we ignore defaults), don't write it.
// if it's just the default (and we ignore defaults), don't write it if (ignoreDefaults && mapIter->second == mddIter->defaultValue)
if(ignoreDefaults && mapIter->second == mddIter->defaultValue)
continue; continue;
// try and make paths relative if we can // Try and make paths relative if we can.
std::string value = mapIter->second; std::string value = mapIter->second;
if (mddIter->type == MD_PATH) if (mddIter->type == MD_PATH)
value = Utils::FileSystem::createRelativePath(value, relativeTo, true); 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 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 int MetaDataList::getInt(const std::string& key) const

View file

@ -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 #pragma once
#ifndef ES_APP_META_DATA_H #ifndef ES_APP_META_DATA_H
#define ES_APP_META_DATA_H #define ES_APP_META_DATA_H
@ -7,35 +14,35 @@
namespace pugi { class xml_node; } namespace pugi { class xml_node; }
enum MetaDataType enum MetaDataType {
{ // Generic types
//generic types
MD_STRING, MD_STRING,
MD_INT, MD_INT,
MD_FLOAT, MD_FLOAT,
MD_BOOL, MD_BOOL,
//specialized types // Specialized types
MD_MULTILINE_STRING, MD_MULTILINE_STRING,
MD_LAUNCHSTRING, MD_LAUNCHSTRING,
MD_PATH, MD_PATH,
MD_RATING, MD_RATING,
MD_DATE, MD_DATE,
MD_TIME //used for lastplayed MD_TIME // Used for lastplayed
}; };
struct MetaDataDecl struct MetaDataDecl {
{
std::string key; std::string key;
MetaDataType type; MetaDataType type;
std::string defaultValue; std::string defaultValue;
bool isStatistic; //if true, ignore values for this metadata // If true, ignore values for this metadata.
std::string displayName; // displayed as this in editors bool isStatistic;
std::string displayPrompt; // phrase displayed in editors when prompted to enter value (currently only for strings) // 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, GAME_METADATA,
FOLDER_METADATA FOLDER_METADATA
}; };
@ -45,8 +52,10 @@ const std::vector<MetaDataDecl>& getMDDByType(MetaDataListType type);
class MetaDataList class MetaDataList
{ {
public: public:
static MetaDataList createFromXML(MetaDataListType type, pugi::xml_node& node, const std::string& relativeTo); static MetaDataList createFromXML(MetaDataListType type,
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults, const std::string& relativeTo) const; pugi::xml_node& node, const std::string& relativeTo);
void appendToXML(pugi::xml_node& parent, bool ignoreDefaults,
const std::string& relativeTo) const;
MetaDataList(MetaDataListType type); MetaDataList(MetaDataListType type);
@ -65,6 +74,7 @@ public:
private: private:
MetaDataListType mType; MetaDataListType mType;
std::map<std::string, std::string> mMap; std::map<std::string, std::string> mMap;
std::string mNoResult = "";
bool mWasChanged; bool mWasChanged;
}; };

View file

@ -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 "SystemData.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
@ -18,31 +27,44 @@
std::vector<SystemData*> SystemData::sSystemVector; std::vector<SystemData*> SystemData::sSystemVector;
SystemData::SystemData(const std::string& name, const std::string& fullName, SystemEnvironmentData* envData, const std::string& themeFolder, bool CollectionSystem) : SystemData::SystemData(
mName(name), mFullName(fullName), mEnvData(envData), mThemeFolder(themeFolder), mIsCollectionSystem(CollectionSystem), mIsGameSystem(true) 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(); mFilterIndex = new FileFilterIndex();
// if it's an actual system, initialize it, if not, just create the data structure // If it's an actual system, initialize it, if not, just create the data structure.
if(!CollectionSystem) if (!CollectionSystem) {
{
mRootFolder = new FileData(FOLDER, mEnvData->mStartPath, mEnvData, this); mRootFolder = new FileData(FOLDER, mEnvData->mStartPath, mEnvData, this);
mRootFolder->metadata.set("name", mFullName); mRootFolder->metadata.set("name", mFullName);
if(!Settings::getInstance()->getBool("ParseGamelistOnly")) if (!Settings::getInstance()->getBool("ParseGamelistOnly"))
populateFolder(mRootFolder); populateFolder(mRootFolder);
if(!Settings::getInstance()->getBool("IgnoreGamelist")) if (!Settings::getInstance()->getBool("IgnoreGamelist"))
parseGamelist(this); parseGamelist(this);
mRootFolder->sort(FileSorts::SortTypes.at(0)); setupSystemSortType(mRootFolder);
mRootFolder->sort(getSortTypeFromString(mRootFolder->getSortTypeString()),
Settings::getInstance()->getBool("FavoritesFirst"));
indexAllGameFilters(mRootFolder); indexAllGameFilters(mRootFolder);
} }
else else {
{ // Virtual systems are updated afterwards by CollectionSystemManager.
// virtual systems are updated afterwards, we're just creating the data structure // We're just creating the data structure here.
mRootFolder = new FileData(FOLDER, "" + name, mEnvData, this); mRootFolder = new FileData(FOLDER, "" + name, mEnvData, this);
setupSystemSortType(mRootFolder);
} }
setIsGameSystemStatus(); setIsGameSystemStatus();
loadTheme(); loadTheme();
@ -50,7 +72,7 @@ SystemData::SystemData(const std::string& name, const std::string& fullName, Sys
SystemData::~SystemData() SystemData::~SystemData()
{ {
if(Settings::getInstance()->getString("SaveGamelistsMode") == "on exit") if (Settings::getInstance()->getString("SaveGamelistsMode") == "on exit")
writeMetaData(); writeMetaData();
delete mRootFolder; delete mRootFolder;
@ -59,27 +81,25 @@ SystemData::~SystemData()
void SystemData::setIsGameSystemStatus() void SystemData::setIsGameSystemStatus()
{ {
// we exclude non-game systems from specific operations (i.e. the "RetroPie" system, at least) // 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 // If/when there are more in the future, maybe this can be a more complex method, with a proper
// but for now a simple string comparison is more performant // list but for now a simple string comparison is more performant.
mIsGameSystem = (mName != "retropie"); mIsGameSystem = (mName != "retropie");
} }
void SystemData::populateFolder(FileData* folder) void SystemData::populateFolder(FileData* folder)
{ {
const std::string& folderPath = folder->getPath(); 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!"; LOG(LogWarning) << "Error - folder with path \"" << folderPath << "\" is not a directory!";
return; return;
} }
//make sure that this isn't a symlink to a thing we already have // Make sure that this isn't a symlink to an object we already have.
if(Utils::FileSystem::isSymlink(folderPath)) if (Utils::FileSystem::isSymlink(folderPath)) {
{ // If this symlink resolves to somewhere that's at the beginning of our
//if this symlink resolves to somewhere that's at the beginning of our path, it's gonna recurse // path, it's going to create a recursive loop. Make sure to avoid this.
if(folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0) if (folderPath.find(Utils::FileSystem::getCanonicalPath(folderPath)) == 0) {
{
LOG(LogWarning) << "Skipping infinitely recursive symlink \"" << folderPath << "\""; LOG(LogWarning) << "Skipping infinitely recursive symlink \"" << folderPath << "\"";
return; return;
} }
@ -90,42 +110,42 @@ void SystemData::populateFolder(FileData* folder)
bool isGame; bool isGame;
bool showHidden = Settings::getInstance()->getBool("ShowHiddenFiles"); bool showHidden = Settings::getInstance()->getBool("ShowHiddenFiles");
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(folderPath); 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; filePath = *it;
// skip hidden files and folders // Skip hidden files and folders.
if(!showHidden && Utils::FileSystem::isHidden(filePath)) if (!showHidden && Utils::FileSystem::isHidden(filePath))
continue; continue;
//this is a little complicated because we allow a list of extensions to be defined (delimited with a space) // This is a little complicated because we allow a list
//we first get the extension of the file itself: // of extensions to be defined (delimited with a space).
// We first get the extension of the file itself:
extension = Utils::FileSystem::getExtension(filePath); extension = Utils::FileSystem::getExtension(filePath);
//fyi, folders *can* also match the extension and be added as games - this is mostly just to support higan // FYI, folders *can* also match the extension and be added as games.
//see issue #75: https://github.com/Aloshi/EmulationStation/issues/75 // This is mostly just to support higan.
// See issue #75: https://github.com/Aloshi/EmulationStation/issues/75
isGame = false; 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); FileData* newGame = new FileData(GAME, filePath, mEnvData, this);
// preventing new arcade assets to be added // Prevent new arcade assets from being added.
if(!newGame->isArcadeAsset()) if (!newGame->isArcadeAsset()) {
{
folder->addChild(newGame); folder->addChild(newGame);
isGame = true; isGame = true;
} }
} }
//add directories that also do not match an extension as folders // Add directories that also do not match an extension as folders.
if(!isGame && Utils::FileSystem::isDirectory(filePath)) if (!isGame && Utils::FileSystem::isDirectory(filePath)) {
{
FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this); FileData* newFolder = new FileData(FOLDER, filePath, mEnvData, this);
populateFolder(newFolder); populateFolder(newFolder);
//ignore folders that do not contain games // Ignore folders that do not contain games.
if(newFolder->getChildrenByFilename().size() == 0) if (newFolder->getChildrenByFilename().size() == 0)
delete newFolder; delete newFolder;
else else
folder->addChild(newFolder); folder->addChild(newFolder);
@ -137,12 +157,17 @@ void SystemData::indexAllGameFilters(const FileData* folder)
{ {
const std::vector<FileData*>& children = folder->getChildren(); const std::vector<FileData*>& children = folder->getChildren();
for(std::vector<FileData*>::const_iterator it = children.cbegin(); it != children.cend(); ++it) for (std::vector<FileData*>::const_iterator it = children.cbegin();
{ it != children.cend(); ++it) {
switch((*it)->getType()) switch ((*it)->getType()) {
{ case GAME: {
case GAME: { mFilterIndex->addToIndex(*it); } break; mFilterIndex->addToIndex(*it);
case FOLDER: { indexAllGameFilters(*it); } break; }
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 prevOff = str.find_first_not_of(delims, 0);
size_t off = str.find_first_of(delims, prevOff); 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)); ret.push_back(str.substr(prevOff, off - prevOff));
prevOff = str.find_first_not_of(delims, off); 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; return ret;
} }
//creates systems from information located in a config file // Creates systems from information located in a config file.
bool SystemData::loadConfig() bool SystemData::loadConfig()
{ {
deleteSystems(); deleteSystems();
@ -173,8 +197,7 @@ bool SystemData::loadConfig()
LOG(LogInfo) << "Loading system config file " << path << "..."; 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!"; LOG(LogError) << "es_systems.cfg file does not exist!";
writeExampleConfig(getConfigPath(true)); writeExampleConfig(getConfigPath(true));
return false; return false;
@ -183,80 +206,78 @@ bool SystemData::loadConfig()
pugi::xml_document doc; pugi::xml_document doc;
pugi::xml_parse_result res = doc.load_file(path.c_str()); 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) << "Could not parse es_systems.cfg file!";
LOG(LogError) << res.description(); LOG(LogError) << res.description();
return false; return false;
} }
//actually read the file // Actually read the file.
pugi::xml_node systemList = doc.child("systemList"); pugi::xml_node systemList = doc.child("systemList");
if(!systemList) if (!systemList) {
{
LOG(LogError) << "es_systems.cfg is missing the <systemList> tag!"; LOG(LogError) << "es_systems.cfg is missing the <systemList> tag!";
return false; 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; std::string name, fullname, path, cmd, themeFolder;
name = system.child("name").text().get(); name = system.child("name").text().get();
fullname = system.child("fullname").text().get(); fullname = system.child("fullname").text().get();
path = system.child("path").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()); std::vector<std::string> extensions = readList(system.child("extension").text().get());
cmd = system.child("command").text().get(); cmd = system.child("command").text().get();
// platform id list // Platform ID list
const char* platformList = system.child("platform").text().get(); const char* platformList = system.child("platform").text().get();
std::vector<std::string> platformStrs = readList(platformList); std::vector<std::string> platformStrs = readList(platformList);
std::vector<PlatformIds::PlatformId> platformIds; 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(); const char* str = it->c_str();
PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str); PlatformIds::PlatformId platformId = PlatformIds::getPlatformId(str);
if(platformId == PlatformIds::PLATFORM_IGNORE) if (platformId == PlatformIds::PLATFORM_IGNORE) {
{ // When platform is PLATFORM_IGNORE, do not allow other platforms.
// when platform is ignore, do not allow other platforms
platformIds.clear(); platformIds.clear();
platformIds.push_back(platformId); platformIds.push_back(platformId);
break; break;
} }
// if there appears to be an actual platform ID supplied but it didn't match the list, warn // If there appears to be an actual platform ID supplied
if(str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN) // but it didn't match the list, generate a warning.
LOG(LogWarning) << " Unknown platform for system \"" << name << "\" (platform \"" << str << "\" from list \"" << platformList << "\")"; if (str != NULL && str[0] != '\0' && platformId == PlatformIds::PLATFORM_UNKNOWN)
else if(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); platformIds.push_back(platformId);
} }
// theme folder // Theme folder.
themeFolder = system.child("theme").text().as_string(name.c_str()); themeFolder = system.child("theme").text().as_string(name.c_str());
//validate // Validate.
if(name.empty() || path.empty() || extensions.empty() || cmd.empty()) if (name.empty() || path.empty() || extensions.empty() || cmd.empty()) {
{ LOG(LogError) << "System \"" << name <<
LOG(LogError) << "System \"" << name << "\" is missing name, path, extension, or command!"; "\" is missing name, path, extension, or command!";
continue; continue;
} }
//convert path to generic directory seperators // Convert path to generic directory seperators.
path = Utils::FileSystem::getGenericPath(path); path = Utils::FileSystem::getGenericPath(path);
//expand home symbol if the startpath contains ~ // Expand home symbol if the startpath contains ~
if(path[0] == '~') if (path[0] == '~')
{ {
path.erase(0, 1); path.erase(0, 1);
path.insert(0, Utils::FileSystem::getHomePath()); path.insert(0, Utils::FileSystem::getHomePath());
} }
//create the system runtime environment data // Create the system runtime environment data.
SystemEnvironmentData* envData = new SystemEnvironmentData; SystemEnvironmentData* envData = new SystemEnvironmentData;
envData->mStartPath = path; envData->mStartPath = path;
envData->mSearchExtensions = extensions; envData->mSearchExtensions = extensions;
@ -264,11 +285,11 @@ bool SystemData::loadConfig()
envData->mPlatformIds = platformIds; envData->mPlatformIds = platformIds;
SystemData* newSys = new SystemData(name, fullname, envData, themeFolder); 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."; LOG(LogWarning) << "System \"" << name << "\" has no games! Ignoring it.";
delete newSys; delete newSys;
}else{ }
else {
sSystemVector.push_back(newSys); sSystemVector.push_back(newSys);
} }
} }
@ -325,17 +346,16 @@ void SystemData::writeExampleConfig(const std::string& path)
void SystemData::deleteSystems() 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); delete sSystemVector.at(i);
}
sSystemVector.clear(); sSystemVector.clear();
} }
std::string SystemData::getConfigPath(bool forWrite) std::string SystemData::getConfigPath(bool forWrite)
{ {
std::string path = Utils::FileSystem::getHomePath() + "/.emulationstation/es_systems.cfg"; 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 path;
return "/etc/emulationstation/es_systems.cfg"; return "/etc/emulationstation/es_systems.cfg";
@ -352,12 +372,13 @@ SystemData* SystemData::getNext() const
{ {
std::vector<SystemData*>::const_iterator it = getIterator(); 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 { do {
it++; it++;
if (it == sSystemVector.cend()) if (it == sSystemVector.cend())
it = sSystemVector.cbegin(); it = sSystemVector.cbegin();
} while (!(*it)->isVisible()); } 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; return *it;
} }
@ -366,12 +387,13 @@ SystemData* SystemData::getPrev() const
{ {
std::vector<SystemData*>::const_reverse_iterator it = getRevIterator(); 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 { do {
it++; it++;
if (it == sSystemVector.crend()) if (it == sSystemVector.crend())
it = sSystemVector.crbegin(); it = sSystemVector.crbegin();
} while (!(*it)->isVisible()); } 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; return *it;
} }
@ -381,13 +403,15 @@ std::string SystemData::getGamelistPath(bool forWrite) const
std::string filePath; std::string filePath;
filePath = mRootFolder->getPath() + "/gamelist.xml"; filePath = mRootFolder->getPath() + "/gamelist.xml";
if(Utils::FileSystem::exists(filePath)) if (Utils::FileSystem::exists(filePath))
return filePath; return filePath;
filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" + mName + "/gamelist.xml"; filePath = Utils::FileSystem::getHomePath() + "/.emulationstation/gamelists/" +
if(forWrite) // make sure the directory exists if we're going to write to it, or crashes will happen 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)); Utils::FileSystem::createDirectory(Utils::FileSystem::getParent(filePath));
if(forWrite || Utils::FileSystem::exists(filePath)) if (forWrite || Utils::FileSystem::exists(filePath))
return filePath; return filePath;
return "/etc/emulationstation/gamelists/" + mName + "/gamelist.xml"; return "/etc/emulationstation/gamelists/" + mName + "/gamelist.xml";
@ -395,24 +419,25 @@ std::string SystemData::getGamelistPath(bool forWrite) const
std::string SystemData::getThemePath() 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 // 1. [SYSTEM_PATH]/theme.xml
// 2. system theme from currently selected theme set [CURRENT_THEME_PATH]/[SYSTEM]/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 // 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"; std::string localThemePath = mRootFolder->getPath() + "/theme.xml";
if(Utils::FileSystem::exists(localThemePath)) if (Utils::FileSystem::exists(localThemePath))
return 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); localThemePath = ThemeData::getThemeFromCurrentSet(mThemeFolder);
if (Utils::FileSystem::exists(localThemePath)) if (Utils::FileSystem::exists(localThemePath))
return localThemePath; return localThemePath;
// not system theme, try default system theme in theme set // Not system theme, try default system theme in theme set.
localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) + "/theme.xml"; localThemePath = Utils::FileSystem::getParent(Utils::FileSystem::getParent(localThemePath)) +
"/theme.xml";
return localThemePath; return localThemePath;
} }
@ -429,32 +454,27 @@ unsigned int SystemData::getGameCount() const
SystemData* SystemData::getRandomSystem() 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; 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()) 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)); int target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++) for (auto it = sSystemVector.cbegin(); it != sSystemVector.cend(); it++)
{ {
if ((*it)->isGameSystem()) if ((*it)->isGameSystem()) {
{
if (target > 0) if (target > 0)
{
target--; target--;
}
else else
{
return (*it); return (*it);
}
} }
} }
// if we end up here, there is no valid system // If we end up here, there is no valid system.
return NULL; return NULL;
} }
@ -463,10 +483,12 @@ FileData* SystemData::getRandomGame()
std::vector<FileData*> list = mRootFolder->getFilesRecursive(GAME, true); std::vector<FileData*> list = mRootFolder->getFilesRecursive(GAME, true);
unsigned int total = (int)list.size(); unsigned int total = (int)list.size();
int target = 0; int target = 0;
// get random number in range
// Get a random number in range.
if (total == 0) if (total == 0)
return NULL; return NULL;
target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1)); target = (int)Math::round((std::rand() / (float)RAND_MAX) * (total - 1));
return list.at(target); return list.at(target);
} }
@ -481,36 +503,54 @@ void SystemData::loadTheme()
std::string path = getThemePath(); 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; return;
try try {
{ // Build map with system variables for theme to use.
// build map with system variables for theme to use,
std::map<std::string, std::string> sysData; 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.name", getName()));
sysData.insert(std::pair<std::string, std::string>("system.theme", getThemeFolder())); sysData.insert(std::pair<std::string, std::string>("system.theme", getThemeFolder()));
sysData.insert(std::pair<std::string, std::string>("system.fullName", getFullName())); sysData.insert(std::pair<std::string, std::string>("system.fullName", getFullName()));
mTheme->loadFile(sysData, path); mTheme->loadFile(sysData, path);
} catch(ThemeException& e) }
{ catch (ThemeException& e) {
LOG(LogError) << e.what(); LOG(LogError) << e.what();
mTheme = std::make_shared<ThemeData>(); // reset to empty mTheme = std::make_shared<ThemeData>(); // Reset to empty.
} }
} }
void SystemData::writeMetaData() { void SystemData::writeMetaData() {
if(Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem) if (Settings::getInstance()->getBool("IgnoreGamelist") || mIsCollectionSystem)
return; return;
//save changed game data back to xml // Save changed game data back to xml.
updateGamelist(this); updateGamelist(this);
} }
void SystemData::onMetaDataSavePoint() { void SystemData::onMetaDataSavePoint() {
if(Settings::getInstance()->getString("SaveGamelistsMode") != "always") if (Settings::getInstance()->getString("SaveGamelistsMode") != "always")
return; return;
writeMetaData(); 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);
}

View file

@ -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 #pragma once
#ifndef ES_APP_SYSTEM_DATA_H #ifndef ES_APP_SYSTEM_DATA_H
#define ES_APP_SYSTEM_DATA_H #define ES_APP_SYSTEM_DATA_H
@ -23,18 +32,27 @@ struct SystemEnvironmentData
class SystemData class SystemData
{ {
public: 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(); ~SystemData();
inline FileData* getRootFolder() const { return mRootFolder; }; inline FileData* getRootFolder() const { return mRootFolder; };
inline const std::string& getName() const { return mName; } inline const std::string& getName() const { return mName; }
inline const std::string& getFullName() const { return mFullName; } inline const std::string& getFullName() const { return mFullName; }
inline const std::string& getStartPath() const { return mEnvData->mStartPath; } 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 const std::string& getThemeFolder() const { return mThemeFolder; }
inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; } inline SystemEnvironmentData* getSystemEnvData() const { return mEnvData; }
inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const { return mEnvData->mPlatformIds; } inline const std::vector<PlatformIds::PlatformId>& getPlatformIds() const
inline bool hasPlatformId(PlatformIds::PlatformId id) { if (!mEnvData) return false; return std::find(mEnvData->mPlatformIds.cbegin(), mEnvData->mPlatformIds.cend(), id) != mEnvData->mPlatformIds.cend(); } { 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; } inline const std::shared_ptr<ThemeData>& getTheme() const { return mTheme; }
@ -46,14 +64,21 @@ public:
unsigned int getDisplayedGameCount() const; unsigned int getDisplayedGameCount() const;
static void deleteSystems(); 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 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; 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_iterator getIterator() const
inline std::vector<SystemData*>::const_reverse_iterator getRevIterator() const { return std::find(sSystemVector.crbegin(), sSystemVector.crend(), this); }; { 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 isCollection() { return mIsCollectionSystem; };
inline bool isGameSystem() { return mIsGameSystem; }; inline bool isGameSystem() { return mIsGameSystem; };
@ -70,6 +95,8 @@ public:
FileFilterIndex* getIndex() { return mFilterIndex; }; FileFilterIndex* getIndex() { return mFilterIndex; };
void onMetaDataSavePoint(); void onMetaDataSavePoint();
void setupSystemSortType(FileData* mRootFolder);
private: private:
bool mIsCollectionSystem; bool mIsCollectionSystem;
bool mIsGameSystem; bool mIsGameSystem;

View file

@ -71,9 +71,9 @@ void GuiCollectionSystemsOptions::initializeMenu()
bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem")); bundleCustomCollections->setState(Settings::getInstance()->getBool("UseCustomCollectionsSystem"));
mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections); mMenu.addWithLabel("GROUP UNTHEMED CUSTOM COLLECTIONS", bundleCustomCollections);
sortAllSystemsSwitch = std::make_shared<SwitchComponent>(mWindow); sortFavFirstCustomSwitch = std::make_shared<SwitchComponent>(mWindow);
sortAllSystemsSwitch->setState(Settings::getInstance()->getBool("SortAllSystems")); sortFavFirstCustomSwitch->setState(Settings::getInstance()->getBool("FavFirstCustom"));
mMenu.addWithLabel("SORT CUSTOM COLLECTIONS AND SYSTEMS", sortAllSystemsSwitch); mMenu.addWithLabel("SORT FAVORITES ON TOP FOR CUSTOM COLLECTIONS", sortFavFirstCustomSwitch);
toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow); toggleSystemNameInCollections = std::make_shared<SwitchComponent>(mWindow);
toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo")); toggleSystemNameInCollections->setState(Settings::getInstance()->getBool("CollectionShowSystemInfo"));
@ -170,8 +170,8 @@ void GuiCollectionSystemsOptions::applySettings()
std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto"); std::string prevAuto = Settings::getInstance()->getString("CollectionSystemsAuto");
std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects()); std::string outCustom = Utils::String::vectorToCommaString(customOptionList->getSelectedObjects());
std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom"); std::string prevCustom = Settings::getInstance()->getString("CollectionSystemsCustom");
bool outSort = sortAllSystemsSwitch->getState(); bool outSort = sortFavFirstCustomSwitch->getState();
bool prevSort = Settings::getInstance()->getBool("SortAllSystems"); bool prevSort = Settings::getInstance()->getBool("FavFirstCustom");
bool outBundle = bundleCustomCollections->getState(); bool outBundle = bundleCustomCollections->getState();
bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem"); bool prevBundle = Settings::getInstance()->getBool("UseCustomCollectionsSystem");
bool prevShow = Settings::getInstance()->getBool("CollectionShowSystemInfo"); 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("CollectionSystemsAuto", newAutoSettings);
Settings::getInstance()->setString("CollectionSystemsCustom", newCustomSettings); 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("UseCustomCollectionsSystem", bundleCustomCollections->getState());
Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState()); Settings::getInstance()->setBool("CollectionShowSystemInfo", toggleSystemNameInCollections->getState());
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();

View file

@ -28,8 +28,8 @@ private:
void exitEditMode(); void exitEditMode();
std::shared_ptr< OptionListComponent<std::string> > autoOptionList; std::shared_ptr< OptionListComponent<std::string> > autoOptionList;
std::shared_ptr< OptionListComponent<std::string> > customOptionList; std::shared_ptr< OptionListComponent<std::string> > customOptionList;
std::shared_ptr<SwitchComponent> sortAllSystemsSwitch;
std::shared_ptr<SwitchComponent> bundleCustomCollections; std::shared_ptr<SwitchComponent> bundleCustomCollections;
std::shared_ptr<SwitchComponent> sortFavFirstCustomSwitch;
std::shared_ptr<SwitchComponent> toggleSystemNameInCollections; std::shared_ptr<SwitchComponent> toggleSystemNameInCollections;
MenuComponent mMenu; MenuComponent mMenu;
SystemData* mSystem; SystemData* mSystem;

View file

@ -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 "GuiGamelistOptions.h"
#include "guis/GuiGamelistFilter.h" #include "guis/GuiGamelistFilter.h"
@ -12,43 +19,50 @@
#include "SystemData.h" #include "SystemData.h"
#include "Sound.h" #include "Sound.h"
GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : GuiComponent(window), GuiGamelistOptions::GuiGamelistOptions(
mSystem(system), mMenu(window, "OPTIONS"), fromPlaceholder(false), mFiltersChanged(false) Window* window,
SystemData* system)
: GuiComponent(window),
mSystem(system),
mMenu(window, "OPTIONS"),
fromPlaceholder(false),
mFiltersChanged(false)
{ {
addChild(&mMenu); 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(); FileData* file = getGamelist()->getCursor();
fromPlaceholder = file->isPlaceHolder(); fromPlaceholder = file->isPlaceHolder();
ComponentListRow row; 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) { if (!fromPlaceholder) {
// jump to letter // Jump to letter.
row.elements.clear(); row.elements.clear();
// define supported character range // Define supported character range.
// this range includes all numbers, capital letters, and most reasonable symbols // This range includes all numbers, capital letters, and most reasonable symbols.
char startChar = '!'; char startChar = '!';
char endChar = '_'; char endChar = '_';
char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]); char curChar = (char)toupper(getGamelist()->getCursor()->getSortName()[0]);
if(curChar < startChar || curChar > endChar) if (curChar < startChar || curChar > endChar)
curChar = startChar; curChar = startChar;
mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO...", false); mJumpToLetterList = std::make_shared<LetterList>(mWindow, "JUMP TO...", false);
if(Settings::getInstance()->getBool("FavoritesFirst") && system->getName() != "favorites" && system->getName() != "recent") if (mFavoritesSorting && system->getName() != "favorites" &&
{ system->getName() != "recent") {
// set firstFavorite to the numerical entry of the first favorite game in the list // Check whether the first game in the list is a favorite, if it's
// if no favorites are found set it to -1 // not, then there are no favorites currently visible in this gamelist.
findFirstFavorite(); if (getGamelist()->getCursor()->getParent()->getChildrenListToDisplay()[0]->
getFavorite()) {
// 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 (getGamelist()->getCursor()->getFavorite()) if (getGamelist()->getCursor()->getFavorite())
mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1); mJumpToLetterList->add(FAVORITE_CHAR, FAVORITE_CHAR, 1);
else else
@ -56,117 +70,147 @@ GuiGamelistOptions::GuiGamelistOptions(Window* window, SystemData* system) : Gui
} }
} }
for (char c = startChar; c <= endChar; c++) for (char c = startChar; c <= endChar; c++) {
{ // Check if c is a valid first letter in the current list.
// check if c is a valid first letter in current list const std::vector<FileData*>& files =
const std::vector<FileData*>& files = getGamelist()->getCursor()->getParent()->getChildrenListToDisplay(); getGamelist()->getCursor()->getParent()->getChildrenListToDisplay();
for (auto file : files) for (auto file : files) {
{
char candidate = (char)toupper(file->getSortName()[0]); char candidate = (char)toupper(file->getSortName()[0]);
if (c == candidate) if (c == candidate) {
{ // If the game is a favorite, continue to the next entry in the list.
// if the game is a favorite, continue to the next entry in the list if (mFavoritesSorting && system->getName() != "favorites" &&
if (firstFavorite != -1 && file->getFavorite()) system->getName() != "recent" && file->getFavorite())
continue; 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; 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.addElement(mJumpToLetterList, false);
row.input_handler = [&](InputConfig* config, Input input) { row.input_handler = [&](InputConfig* config, Input input) {
if(config->isMappedTo("a", input) && input.value) if (config->isMappedTo("a", input) && input.value) {
{ navigationsounds.playThemeNavigationSound(SCROLLSOUND);
if(mJumpToLetterList->getSelected() == FAVORITE_CHAR) if (mJumpToLetterList->getSelected() == FAVORITE_CHAR)
{ jumpToFirstRow();
navigationsounds.playThemeNavigationSound(SCROLLSOUND); else
jumpToFirstFavorite(); jumpToLetter();
return true;
}
navigationsounds.playThemeNavigationSound(SCROLLSOUND);
jumpToLetter();
return true; return true;
} }
else if(mJumpToLetterList->input(config, input)) else if (mJumpToLetterList->input(config, input)) {
{
return true; return true;
} }
return false; 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); mListSort = std::make_shared<SortList>(mWindow, "SORT GAMES BY", false);
for(unsigned int i = 0; i < FileSorts::SortTypes.size(); i++) FileData* root = mSystem->getRootFolder();
{ std::string sortType = root->getSortTypeString();
const FileData::SortType& sort = FileSorts::SortTypes.at(i);
mListSort->add(sort.description, &sort, i == 0); // TODO - actually make the sort type persistent
}
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.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.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openGamelistFilter, this));
mMenu.addRow(row); 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() && if (UIModeController::getInstance()->isUIModeFull() &&
((customCollections.find(system->getName()) != customCollections.cend() && CollectionSystemManager::get()->getEditingCollection() != system->getName()) || ((customCollections.find(system->getName()) != customCollections.cend() &&
CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() == system->getName())) CollectionSystemManager::get()->getEditingCollection() != system->getName()) ||
{ CollectionSystemManager::get()->getCustomCollectionsBundle()->getName() ==
system->getName())) {
row.elements.clear(); 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)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::startEditMode, this));
mMenu.addRow(row); mMenu.addRow(row);
} }
if(UIModeController::getInstance()->isUIModeFull() && CollectionSystemManager::get()->isEditing()) if (UIModeController::getInstance()->isUIModeFull() &&
{ CollectionSystemManager::get()->isEditing()) {
row.elements.clear(); 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)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::exitEditMode, this));
mMenu.addRow(row); 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.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.addElement(makeArrow(mWindow), false);
row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this)); row.makeAcceptInputHandler(std::bind(&GuiGamelistOptions::openMetaDataEd, this));
mMenu.addRow(row); mMenu.addRow(row);
} }
// center the menu // Center the menu.
setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); 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() GuiGamelistOptions::~GuiGamelistOptions()
{ {
// apply sort
if (!fromPlaceholder) { if (!fromPlaceholder) {
FileData* root = mSystem->getRootFolder(); FileData* root = mSystem->getRootFolder();
root->sort(*mListSort->getSelected()); // will also recursively sort children
// notify that the root folder was sorted // If a new sorting type was selected, then sort and update mSortTypeString for the system.
getGamelist()->onFileChanged(root, FILE_SORTED); 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) if (mFiltersChanged) {
{ // Only reload full view if we came from a placeholder as we need to
// only reload full view if we came from a placeholder // re-display the remaining elements for whatever new game is selected.
// as we need to re-display the remaining elements for whatever new
// game is selected
ViewController::get()->reloadGameListView(mSystem); ViewController::get()->reloadGameListView(mSystem);
} }
} }
@ -181,20 +225,16 @@ void GuiGamelistOptions::openGamelistFilter()
void GuiGamelistOptions::startEditMode() void GuiGamelistOptions::startEditMode()
{ {
std::string editingSystem = mSystem->getName(); 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 // Need to check if we're editing the collections bundle,
if(editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) // as we will want to edit the selected collection within.
{ if (editingSystem == CollectionSystemManager::get()->getCustomCollectionsBundle()->getName()) {
FileData* file = getGamelist()->getCursor(); 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) if (file->getType() == FOLDER)
{
editingSystem = file->getName(); editingSystem = file->getName();
}
else 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(); editingSystem = file->getSystem()->getName();
}
} }
CollectionSystemManager::get()->setEditMode(editingSystem); CollectionSystemManager::get()->setEditMode(editingSystem);
delete this; delete this;
@ -208,8 +248,8 @@ void GuiGamelistOptions::exitEditMode()
void GuiGamelistOptions::openMetaDataEd() void GuiGamelistOptions::openMetaDataEd()
{ {
// open metadata editor // Open metadata editor.
// get the FileData that hosts the original metadata // Get the FileData that holds the original metadata.
FileData* file = getGamelist()->getCursor()->getSourceFileData(); FileData* file = getGamelist()->getCursor()->getSourceFileData();
ScraperSearchParams p; ScraperSearchParams p;
p.game = file; p.game = file;
@ -217,106 +257,63 @@ void GuiGamelistOptions::openMetaDataEd()
std::function<void()> deleteBtnFunc; std::function<void()> deleteBtnFunc;
if (file->getType() == FOLDER) if (file->getType() == FOLDER) {
{
deleteBtnFunc = NULL; deleteBtnFunc = NULL;
} }
else else {
{
deleteBtnFunc = [this, file] { deleteBtnFunc = [this, file] {
CollectionSystemManager::get()->deleteCollectionFiles(file); CollectionSystemManager::get()->deleteCollectionFiles(file);
ViewController::get()->getGameListView(file->getSystem()).get()->remove(file, true); 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()), mWindow->pushGui(new GuiMetaDataEd(mWindow, &file->metadata, file->metadata.getMDD(), p,
std::bind(&IGameListView::onFileChanged, ViewController::get()->getGameListView(file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc)); Utils::FileSystem::getFileName(file->getPath()), std::bind(
&IGameListView::onFileChanged, ViewController::get()->getGameListView(
file->getSystem()).get(), file, FILE_METADATA_CHANGED), deleteBtnFunc));
} }
void GuiGamelistOptions::jumpToLetter() void GuiGamelistOptions::jumpToLetter()
{ {
char letter = mJumpToLetterList->getSelected()[0]; char letter = mJumpToLetterList->getSelected()[0];
IGameListView* gamelist = getGamelist();
// this is a really shitty way to get a list of files // Get first row of the gamelist.
const std::vector<FileData*>& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); const std::vector<FileData*>& files = getGamelist()->getCursor()->
getParent()->getChildrenListToDisplay();
long min = 0; for (unsigned int i = 0; i < files.size(); i++) {
long max = (long)files.size() - 1; if (mFavoritesSorting && mSystem->getName() != "favorites") {
long mid = 0; if ((char)toupper(files.at(i)->getSortName()[0]) ==
letter && !files.at(i)->getFavorite()) {
while(max >= min) getGamelist()->setCursor(files.at(i));
{ break;
mid = ((max - min) / 2) + min; }
}
// game somehow has no first character to check else {
if(files.at(mid)->getName().empty()) if ((char)toupper(files.at(i)->getSortName()[0]) == letter) {
continue; getGamelist()->setCursor(files.at(i));
break;
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++;
} }
break;
} }
} }
gamelist->setCursor(files.at(mid));
delete this; delete this;
} }
void GuiGamelistOptions::findFirstFavorite() void GuiGamelistOptions::jumpToFirstRow()
{ {
IGameListView* gamelist = getGamelist(); // Get first row of the gamelist.
const std::vector<FileData*>& files = getGamelist()->getCursor()->
// this is a really shitty way to get a list of files getParent()->getChildrenListToDisplay();
const std::vector<FileData*>& files = gamelist->getCursor()->getParent()->getChildrenListToDisplay(); getGamelist()->setCursor(files.at(0));
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));
delete this; delete this;
} }
bool GuiGamelistOptions::input(InputConfig* config, Input input) 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; delete this;
return true; return true;
} }

View file

@ -1,3 +1,10 @@
//
// GuiGamelistOptions.h
//
// Gamelist options menu for the 'Jump to...' quick selector,
// game sorting, game filters, and metadata edit.
//
#pragma once #pragma once
#ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H #ifndef ES_APP_GUIS_GUI_GAME_LIST_OPTIONS_H
#define 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 openMetaDataEd();
void startEditMode(); void startEditMode();
void exitEditMode(); void exitEditMode();
void jumpToLetter(); void jumpToLetter();
void findFirstFavorite(); void jumpToFirstRow();
void jumpToFirstFavorite();
const std::string FAVORITE_CHAR = "\uF005"; const std::string FAVORITE_CHAR = "\uF005";
long firstFavorite = -1;
MenuComponent mMenu; MenuComponent mMenu;
@ -43,6 +49,7 @@ private:
SystemData* mSystem; SystemData* mSystem;
IGameListView* getGamelist(); IGameListView* getGamelist();
bool mFavoritesSorting;
bool fromPlaceholder; bool fromPlaceholder;
bool mFiltersChanged; bool mFiltersChanged;
}; };

View file

@ -1,3 +1,9 @@
//
// GuiMenu.cpp
//
// Main menu.
//
#include "guis/GuiMenu.h" #include "guis/GuiMenu.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
@ -11,6 +17,7 @@
#include "guis/GuiSettings.h" #include "guis/GuiSettings.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "views/gamelist/IGameListView.h"
#include "CollectionSystemManager.h" #include "CollectionSystemManager.h"
#include "EmulationStation.h" #include "EmulationStation.h"
#include "Scripting.h" #include "Scripting.h"
@ -20,7 +27,11 @@
#include <algorithm> #include <algorithm>
#include "platform.h" #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(); 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(); }); addEntry("SOUND SETTINGS", 0x777777FF, true, [this] { openSoundSettings(); });
if (isFullUI) if (isFullUI)
addEntry("UI SETTINGS", 0x777777FF, true, [this] { openUISettings(); }); addEntry("UI SETTINGS", 0x777777FF, true, [this] { openUISettings(); });
if (isFullUI) if (isFullUI)
addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] { openCollectionSystemSettings(); }); addEntry("GAME COLLECTION SETTINGS", 0x777777FF, true, [this] {
openCollectionSystemSettings(); });
if (isFullUI) if (isFullUI)
addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherSettings(); }); addEntry("OTHER SETTINGS", 0x777777FF, true, [this] { openOtherSettings(); });
@ -47,38 +58,44 @@ GuiMenu::GuiMenu(Window* window) : GuiComponent(window), mMenu(window, "MAIN MEN
addChild(&mMenu); addChild(&mMenu);
addVersionInfo(); addVersionInfo();
setSize(mMenu.getSize()); 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() void GuiMenu::openScraperSettings()
{ {
auto s = new GuiSettings(mWindow, "SCRAPER"); auto s = new GuiSettings(mWindow, "SCRAPER");
// scrape from // Scrape from.
auto scraper_list = std::make_shared< OptionListComponent< std::string > >(mWindow, "SCRAPE FROM", false); auto scraper_list = std::make_shared< OptionListComponent< std::string >
>(mWindow, "SCRAPE FROM", false);
std::vector<std::string> scrapers = getScraperList(); 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. // Select either the first entry of the one read from the settings,
for(auto it = scrapers.cbegin(); it != scrapers.cend(); it++) // 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")); scraper_list->add(*it, *it, *it == Settings::getInstance()->getString("Scraper"));
s->addWithLabel("SCRAPE FROM", scraper_list); 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); auto scrape_ratings = std::make_shared<SwitchComponent>(mWindow);
scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings")); scrape_ratings->setState(Settings::getInstance()->getBool("ScrapeRatings"));
s->addWithLabel("SCRAPE RATINGS", scrape_ratings); 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; ComponentListRow row;
auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); }; auto openScrapeNow = [this] { mWindow->pushGui(new GuiScraperStart(mWindow)); };
std::function<void()> openAndSave = openScrapeNow; std::function<void()> openAndSave = openScrapeNow;
openAndSave = [s, openAndSave] { s->save(); openAndSave(); }; openAndSave = [s, openAndSave] { s->save(); openAndSave(); };
row.makeAcceptInputHandler(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); auto bracket = makeArrow(mWindow);
row.addElement(scrape_now, true); row.addElement(scrape_now, true);
row.addElement(bracket, false); row.addElement(bracket, false);
@ -91,24 +108,25 @@ void GuiMenu::openSoundSettings()
{ {
auto s = new GuiSettings(mWindow, "SOUND SETTINGS"); auto s = new GuiSettings(mWindow, "SOUND SETTINGS");
// volume // System volume.
auto volume = std::make_shared<SliderComponent>(mWindow, 0.f, 100.f, 1.f, "%"); auto volume = std::make_shared<SliderComponent>(mWindow, 0.f, 100.f, 1.f, "%");
volume->setValue((float)VolumeControl::getInstance()->getVolume()); volume->setValue((float)VolumeControl::getInstance()->getVolume());
s->addWithLabel("SYSTEM VOLUME", volume); 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__) #if defined(__linux__)
// audio card // 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; std::vector<std::string> audio_cards;
#ifdef _RPI_ #ifdef _RPI_
// RPi Specific Audio Cards // RPi Specific Audio Cards
audio_cards.push_back("local"); audio_cards.push_back("local");
audio_cards.push_back("hdmi"); audio_cards.push_back("hdmi");
audio_cards.push_back("both"); audio_cards.push_back("both");
#endif #endif
audio_cards.push_back("default"); audio_cards.push_back("default");
audio_cards.push_back("sysdefault"); audio_cards.push_back("sysdefault");
audio_cards.push_back("dmix"); audio_cards.push_back("dmix");
@ -116,11 +134,12 @@ void GuiMenu::openSoundSettings()
audio_cards.push_back("plughw"); audio_cards.push_back("plughw");
audio_cards.push_back("null"); audio_cards.push_back("null");
if (Settings::getInstance()->getString("AudioCard") != "") { 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")); 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); audio_card->add(*ac, *ac, Settings::getInstance()->getString("AudioCard") == *ac);
s->addWithLabel("AUDIO CARD", audio_card); s->addWithLabel("AUDIO CARD", audio_card);
s->addSaveFunc([audio_card] { s->addSaveFunc([audio_card] {
@ -129,8 +148,9 @@ void GuiMenu::openSoundSettings()
VolumeControl::getInstance()->init(); VolumeControl::getInstance()->init();
}); });
// volume control device // Volume control device.
auto vol_dev = std::make_shared< OptionListComponent<std::string> >(mWindow, "AUDIO DEVICE", false); auto vol_dev = std::make_shared< OptionListComponent<std::string>
>(mWindow, "AUDIO DEVICE", false);
std::vector<std::string> transitions; std::vector<std::string> transitions;
transitions.push_back("PCM"); transitions.push_back("PCM");
transitions.push_back("Speaker"); transitions.push_back("Speaker");
@ -138,11 +158,12 @@ void GuiMenu::openSoundSettings()
transitions.push_back("Digital"); transitions.push_back("Digital");
transitions.push_back("Analogue"); transitions.push_back("Analogue");
if (Settings::getInstance()->getString("AudioDevice") != "") { 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")); 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); vol_dev->add(*it, *it, Settings::getInstance()->getString("AudioDevice") == *it);
s->addWithLabel("AUDIO DEVICE", vol_dev); s->addWithLabel("AUDIO DEVICE", vol_dev);
s->addSaveFunc([vol_dev] { s->addSaveFunc([vol_dev] {
@ -152,7 +173,7 @@ void GuiMenu::openSoundSettings()
}); });
#endif #endif
// disable sounds // Disable sounds.
auto sounds_enabled = std::make_shared<SwitchComponent>(mWindow); auto sounds_enabled = std::make_shared<SwitchComponent>(mWindow);
sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds")); sounds_enabled->setState(Settings::getInstance()->getBool("EnableSounds"));
s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled); s->addWithLabel("ENABLE NAVIGATION SOUNDS", sounds_enabled);
@ -170,11 +191,13 @@ void GuiMenu::openSoundSettings()
auto video_audio = std::make_shared<SwitchComponent>(mWindow); auto video_audio = std::make_shared<SwitchComponent>(mWindow);
video_audio->setState(Settings::getInstance()->getBool("VideoAudio")); video_audio->setState(Settings::getInstance()->getBool("VideoAudio"));
s->addWithLabel("ENABLE VIDEO AUDIO", video_audio); 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_ #ifdef _RPI_
// OMX player Audio Device // 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; std::vector<std::string> omx_cards;
// RPi Specific Audio Cards // RPi Specific Audio Cards
omx_cards.push_back("local"); 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:0,0");
omx_cards.push_back("alsa:hw:1,0"); omx_cards.push_back("alsa:hw:1,0");
if (Settings::getInstance()->getString("OMXAudioDev") != "") { 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")); omx_cards.push_back(Settings::getInstance()->getString("OMXAudioDev"));
} }
} }
@ -198,26 +222,25 @@ void GuiMenu::openSoundSettings()
} }
mWindow->pushGui(s); mWindow->pushGui(s);
} }
void GuiMenu::openUISettings() void GuiMenu::openUISettings()
{ {
auto s = new GuiSettings(mWindow, "UI SETTINGS"); auto s = new GuiSettings(mWindow, "UI SETTINGS");
//UI mode // UI mode.
auto UImodeSelection = std::make_shared< OptionListComponent<std::string> >(mWindow, "UI MODE", false); auto UImodeSelection = std::make_shared< OptionListComponent<std::string>
>(mWindow, "UI MODE", false);
std::vector<std::string> UImodes = UIModeController::getInstance()->getUIModes(); std::vector<std::string> UImodes = UIModeController::getInstance()->getUIModes();
for (auto it = UImodes.cbegin(); it != UImodes.cend(); it++) for (auto it = UImodes.cbegin(); it != UImodes.cend(); it++)
UImodeSelection->add(*it, *it, Settings::getInstance()->getString("UIMode") == *it); UImodeSelection->add(*it, *it, Settings::getInstance()->getString("UIMode") == *it);
s->addWithLabel("UI MODE", UImodeSelection); s->addWithLabel("UI MODE", UImodeSelection);
Window* window = mWindow; Window* window = mWindow;
s->addSaveFunc([ UImodeSelection, window] s->addSaveFunc([ UImodeSelection, window] {
{
std::string selectedMode = UImodeSelection->getSelected(); std::string selectedMode = UImodeSelection->getSelected();
if (selectedMode != "Full") if (selectedMode != "Full") {
{ std::string msg = "You are changing the UI to a restricted mode:\n" +
std::string msg = "You are changing the UI to a restricted mode:\n" + selectedMode + "\n"; selectedMode + "\n";
msg += "This will hide most menu-options to prevent changes to the system.\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 += "To unlock and return to the full UI, enter this code: \n";
msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n"; msg += "\"" + UIModeController::getInstance()->getFormattedPassKeyStr() + "\"\n\n";
@ -231,109 +254,114 @@ void GuiMenu::openUISettings()
} }
}); });
// fullscreen mode // Fullscreen mode.
auto fullscreen_mode = std::make_shared< OptionListComponent<std::string> >(mWindow, "FULLSCREEN MODE", false); auto fullscreen_mode = std::make_shared< OptionListComponent<std::string>
>(mWindow, "FULLSCREEN MODE", false);
std::vector<std::string> screenmode; std::vector<std::string> screenmode;
screenmode.push_back("normal"); screenmode.push_back("normal");
screenmode.push_back("borderless"); 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); fullscreen_mode->add(*it, *it, Settings::getInstance()->getString("FullscreenMode") == *it);
s->addWithLabel("FULLSCREEN MODE (REQUIRES RESTART)", fullscreen_mode); s->addWithLabel("FULLSCREEN MODE (REQUIRES RESTART)", fullscreen_mode);
s->addSaveFunc([fullscreen_mode] { s->addSaveFunc([fullscreen_mode] {
if (Settings::getInstance()->getString("FullscreenMode") == "normal" if (Settings::getInstance()->getString("FullscreenMode") == "normal"
&& fullscreen_mode->getSelected() != "normal") && fullscreen_mode->getSelected() != "normal") {
{
Settings::getInstance()->setString("PowerSaverMode", "default"); Settings::getInstance()->setString("PowerSaverMode", "default");
PowerSaver::init(); PowerSaver::init();
} }
Settings::getInstance()->setString("FullscreenMode", fullscreen_mode->getSelected()); Settings::getInstance()->setString("FullscreenMode", fullscreen_mode->getSelected());
}); });
// screensaver // Screensaver.
ComponentListRow screensaver_row; ComponentListRow screensaver_row;
screensaver_row.elements.clear(); 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.addElement(makeArrow(mWindow), false);
screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this)); screensaver_row.makeAcceptInputHandler(std::bind(&GuiMenu::openScreensaverOptions, this));
s->addRow(screensaver_row); 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); auto quick_sys_select = std::make_shared<SwitchComponent>(mWindow);
quick_sys_select->setState(Settings::getInstance()->getBool("QuickSystemSelect")); quick_sys_select->setState(Settings::getInstance()->getBool("QuickSystemSelect"));
s->addWithLabel("QUICK SYSTEM SELECT", quick_sys_select); 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); auto move_carousel = std::make_shared<SwitchComponent>(mWindow);
move_carousel->setState(Settings::getInstance()->getBool("MoveCarousel")); move_carousel->setState(Settings::getInstance()->getBool("MoveCarousel"));
s->addWithLabel("CAROUSEL TRANSITIONS", move_carousel); s->addWithLabel("CAROUSEL TRANSITIONS", move_carousel);
s->addSaveFunc([move_carousel] { s->addSaveFunc([move_carousel] {
if (move_carousel->getState() if (move_carousel->getState()
&& !Settings::getInstance()->getBool("MoveCarousel") && !Settings::getInstance()->getBool("MoveCarousel")
&& PowerSaver::getMode() == PowerSaver::INSTANT) && PowerSaver::getMode() == PowerSaver::INSTANT) {
{
Settings::getInstance()->setString("PowerSaverMode", "default"); Settings::getInstance()->setString("PowerSaverMode", "default");
PowerSaver::init(); PowerSaver::init();
} }
Settings::getInstance()->setBool("MoveCarousel", move_carousel->getState()); Settings::getInstance()->setBool("MoveCarousel", move_carousel->getState());
}); });
// transition style // Transition style.
auto transition_style = std::make_shared< OptionListComponent<std::string> >(mWindow, "TRANSITION STYLE", false); auto transition_style = std::make_shared< OptionListComponent<std::string>
>(mWindow, "TRANSITION STYLE", false);
std::vector<std::string> transitions; std::vector<std::string> transitions;
transitions.push_back("fade"); transitions.push_back("fade");
transitions.push_back("slide"); transitions.push_back("slide");
transitions.push_back("instant"); transitions.push_back("instant");
for(auto it = transitions.cbegin(); it != transitions.cend(); it++) for (auto it = transitions.cbegin(); it != transitions.cend(); it++)
transition_style->add(*it, *it, Settings::getInstance()->getString("TransitionStyle") == *it); transition_style->add(*it, *it, Settings::getInstance()->
getString("TransitionStyle") == *it);
s->addWithLabel("TRANSITION STYLE", transition_style); s->addWithLabel("TRANSITION STYLE", transition_style);
s->addSaveFunc([transition_style] { s->addSaveFunc([transition_style] {
if (Settings::getInstance()->getString("TransitionStyle") == "instant" if (Settings::getInstance()->getString("TransitionStyle") == "instant"
&& transition_style->getSelected() != "instant" && transition_style->getSelected() != "instant"
&& PowerSaver::getMode() == PowerSaver::INSTANT) && PowerSaver::getMode() == PowerSaver::INSTANT) {
{
Settings::getInstance()->setString("PowerSaverMode", "default"); Settings::getInstance()->setString("PowerSaverMode", "default");
PowerSaver::init(); PowerSaver::init();
} }
Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected()); Settings::getInstance()->setString("TransitionStyle", transition_style->getSelected());
}); });
// theme set // Theme selection.
auto themeSets = ThemeData::getThemeSets(); auto themeSets = ThemeData::getThemeSets();
if(!themeSets.empty()) if (!themeSets.empty())
{ {
std::map<std::string, ThemeSet>::const_iterator selectedSet = themeSets.find(Settings::getInstance()->getString("ThemeSet")); std::map<std::string, ThemeSet>::const_iterator selectedSet =
if(selectedSet == themeSets.cend()) themeSets.find(Settings::getInstance()->getString("ThemeSet"));
if (selectedSet == themeSets.cend())
selectedSet = themeSets.cbegin(); selectedSet = themeSets.cbegin();
auto theme_set = std::make_shared< OptionListComponent<std::string> >(mWindow, "THEME SET", false); auto theme_set = std::make_shared< OptionListComponent<std::string>
for(auto it = themeSets.cbegin(); it != themeSets.cend(); it++) >(mWindow, "THEME SET", false);
for (auto it = themeSets.cbegin(); it != themeSets.cend(); it++)
theme_set->add(it->first, it->first, it == selectedSet); theme_set->add(it->first, it->first, it == selectedSet);
s->addWithLabel("THEME SET", theme_set); s->addWithLabel("THEME SET", theme_set);
Window* window = mWindow; Window* window = mWindow;
s->addSaveFunc([window, theme_set] s->addSaveFunc([window, theme_set] {
{
bool needReload = false; bool needReload = false;
std::string oldTheme = Settings::getInstance()->getString("ThemeSet"); std::string oldTheme = Settings::getInstance()->getString("ThemeSet");
if(oldTheme != theme_set->getSelected()) if (oldTheme != theme_set->getSelected())
needReload = true; needReload = true;
Settings::getInstance()->setString("ThemeSet", theme_set->getSelected()); Settings::getInstance()->setString("ThemeSet", theme_set->getSelected());
if(needReload) if (needReload)
{ {
Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme); Scripting::fireEvent("theme-changed", theme_set->getSelected(), oldTheme);
CollectionSystemManager::get()->updateSystemsList(); CollectionSystemManager::get()->updateSystemsList();
ViewController::get()->goToStart(); 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 // GameList view style.
auto gamelist_style = std::make_shared< OptionListComponent<std::string> >(mWindow, "GAMELIST VIEW STYLE", false); auto gamelist_style = std::make_shared< OptionListComponent<std::string>
>(mWindow, "GAMELIST VIEW STYLE", false);
std::vector<std::string> styles; std::vector<std::string> styles;
styles.push_back("automatic"); styles.push_back("automatic");
styles.push_back("basic"); styles.push_back("basic");
@ -342,92 +370,121 @@ void GuiMenu::openUISettings()
styles.push_back("grid"); styles.push_back("grid");
for (auto it = styles.cbegin(); it != styles.cend(); it++) 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->addWithLabel("GAMELIST VIEW STYLE", gamelist_style);
s->addSaveFunc([gamelist_style] { s->addSaveFunc([gamelist_style] {
bool needReload = false; bool needReload = false;
if (Settings::getInstance()->getString("GamelistViewStyle") != gamelist_style->getSelected()) if (Settings::getInstance()->getString("GamelistViewStyle") !=
gamelist_style->getSelected())
needReload = true; needReload = true;
Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected()); Settings::getInstance()->setString("GamelistViewStyle", gamelist_style->getSelected());
if (needReload) if (needReload)
ViewController::get()->reloadAll(); ViewController::get()->reloadAll();
}); });
// Optionally start in selected system // Optionally start in selected system.
auto systemfocus_list = std::make_shared< OptionListComponent<std::string> >(mWindow, "START ON SYSTEM", false); auto systemfocus_list = std::make_shared< OptionListComponent<std::string>
>(mWindow, "START ON SYSTEM", false);
systemfocus_list->add("NONE", "", Settings::getInstance()->getString("StartupSystem") == ""); 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()) if ("retropie" != (*it)->getName())
{ systemfocus_list->add((*it)->getName(), (*it)->getName(),
systemfocus_list->add((*it)->getName(), (*it)->getName(), Settings::getInstance()->getString("StartupSystem") == (*it)->getName()); Settings::getInstance()->getString("StartupSystem") == (*it)->getName());
}
} }
s->addWithLabel("START ON SYSTEM", systemfocus_list); s->addWithLabel("START ON SYSTEM", systemfocus_list);
s->addSaveFunc([systemfocus_list] { s->addSaveFunc([systemfocus_list] {
Settings::getInstance()->setString("StartupSystem", systemfocus_list->getSelected()); Settings::getInstance()->setString("StartupSystem", systemfocus_list->getSelected());
}); });
// show help // Show help.
auto show_help = std::make_shared<SwitchComponent>(mWindow); auto show_help = std::make_shared<SwitchComponent>(mWindow);
show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts")); show_help->setState(Settings::getInstance()->getBool("ShowHelpPrompts"));
s->addWithLabel("ON-SCREEN HELP", show_help); 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); auto enable_filter = std::make_shared<SwitchComponent>(mWindow);
enable_filter->setState(!Settings::getInstance()->getBool("ForceDisableFilters")); enable_filter->setState(!Settings::getInstance()->getBool("ForceDisableFilters"));
s->addWithLabel("ENABLE FILTERS", enable_filter); s->addWithLabel("ENABLE GAMELIST FILTERS", enable_filter);
s->addSaveFunc([enable_filter] { s->addSaveFunc([enable_filter] {
bool filter_is_enabled = !Settings::getInstance()->getBool("ForceDisableFilters"); bool filter_is_enabled = !Settings::getInstance()->getBool("ForceDisableFilters");
Settings::getInstance()->setBool("ForceDisableFilters", !enable_filter->getState()); 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); auto disable_start = std::make_shared<SwitchComponent>(mWindow);
disable_start->setState(Settings::getInstance()->getBool("DisableKidStartMenu")); disable_start->setState(Settings::getInstance()->getBool("DisableKidStartMenu"));
s->addWithLabel("DISABLE START MENU IN KID MODE", disable_start); 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); auto show_rebootsystem = std::make_shared<SwitchComponent>(mWindow);
show_rebootsystem->setState(Settings::getInstance()->getBool("ShowRebootSystem")); show_rebootsystem->setState(Settings::getInstance()->getBool("ShowRebootSystem"));
s->addWithLabel("SHOW \"REBOOT SYSTEM\" MENU ENTRY", show_rebootsystem); 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); auto show_poweroffsystem = std::make_shared<SwitchComponent>(mWindow);
show_poweroffsystem->setState(Settings::getInstance()->getBool("ShowPoweroffSystem")); show_poweroffsystem->setState(Settings::getInstance()->getBool("ShowPoweroffSystem"));
s->addWithLabel("SHOW \"POWER OFF SYSTEM\" MENU ENTRY", show_poweroffsystem); 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); auto favoritesFirstSwitch = std::make_shared<SwitchComponent>(mWindow);
favoritesFirstSwitch->setState(Settings::getInstance()->getBool("FavoritesFirst")); favoritesFirstSwitch->setState(Settings::getInstance()->getBool("FavoritesFirst"));
s->addWithLabel("SHOW FAVORITES ON TOP OF GAMELIST", favoritesFirstSwitch); s->addWithLabel("SORT FAVORITES ON TOP OF GAMELISTS", favoritesFirstSwitch);
s->addSaveFunc([favoritesFirstSwitch] s->addSaveFunc([favoritesFirstSwitch] {
{
if (Settings::getInstance()->setBool("FavoritesFirst", favoritesFirstSwitch->getState())) 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); mWindow->pushGui(s);
} }
void GuiMenu::openOtherSettings() void GuiMenu::openOtherSettings()
{ {
auto s = new GuiSettings(mWindow, "OTHER SETTINGS"); 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"); auto max_vram = std::make_shared<SliderComponent>(mWindow, 0.f, 1000.f, 10.f, "Mb");
max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM"))); max_vram->setValue((float)(Settings::getInstance()->getInt("MaxVRAM")));
s->addWithLabel("VRAM LIMIT", max_vram); 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 // Power saver.
auto power_saver = std::make_shared< OptionListComponent<std::string> >(mWindow, "POWER SAVER MODES", false); auto power_saver = std::make_shared< OptionListComponent<std::string>
>(mWindow, "POWER SAVER MODES", false);
std::vector<std::string> modes; std::vector<std::string> modes;
modes.push_back("disabled"); modes.push_back("disabled");
modes.push_back("default"); modes.push_back("default");
@ -437,7 +494,8 @@ void GuiMenu::openOtherSettings()
power_saver->add(*it, *it, Settings::getInstance()->getString("PowerSaverMode") == *it); power_saver->add(*it, *it, Settings::getInstance()->getString("PowerSaverMode") == *it);
s->addWithLabel("POWER SAVER MODES", power_saver); s->addWithLabel("POWER SAVER MODES", power_saver);
s->addSaveFunc([this, 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()->setString("TransitionStyle", "instant");
Settings::getInstance()->setBool("MoveCarousel", false); Settings::getInstance()->setBool("MoveCarousel", false);
Settings::getInstance()->setBool("EnableSounds", false); Settings::getInstance()->setBool("EnableSounds", false);
@ -446,15 +504,17 @@ void GuiMenu::openOtherSettings()
PowerSaver::init(); PowerSaver::init();
}); });
// gamelists // Gamelists.
auto gamelistsSaveMode = std::make_shared< OptionListComponent<std::string> >(mWindow, "SAVE METADATA", false); auto gamelistsSaveMode = std::make_shared< OptionListComponent<std::string>
>(mWindow, "SAVE METADATA", false);
std::vector<std::string> saveModes; std::vector<std::string> saveModes;
saveModes.push_back("on exit"); saveModes.push_back("on exit");
saveModes.push_back("always"); saveModes.push_back("always");
saveModes.push_back("never"); saveModes.push_back("never");
for(auto it = saveModes.cbegin(); it != saveModes.cend(); it++) for (auto it = saveModes.cbegin(); it != saveModes.cend(); it++)
gamelistsSaveMode->add(*it, *it, Settings::getInstance()->getString("SaveGamelistsMode") == *it); gamelistsSaveMode->add(*it, *it, Settings::getInstance()->
getString("SaveGamelistsMode") == *it);
s->addWithLabel("SAVE METADATA", gamelistsSaveMode); s->addWithLabel("SAVE METADATA", gamelistsSaveMode);
s->addSaveFunc([gamelistsSaveMode] { s->addSaveFunc([gamelistsSaveMode] {
Settings::getInstance()->setString("SaveGamelistsMode", gamelistsSaveMode->getSelected()); Settings::getInstance()->setString("SaveGamelistsMode", gamelistsSaveMode->getSelected());
@ -463,51 +523,56 @@ void GuiMenu::openOtherSettings()
auto parse_gamelists = std::make_shared<SwitchComponent>(mWindow); auto parse_gamelists = std::make_shared<SwitchComponent>(mWindow);
parse_gamelists->setState(Settings::getInstance()->getBool("ParseGamelistOnly")); parse_gamelists->setState(Settings::getInstance()->getBool("ParseGamelistOnly"));
s->addWithLabel("PARSE GAMESLISTS ONLY", parse_gamelists); 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); auto local_art = std::make_shared<SwitchComponent>(mWindow);
local_art->setState(Settings::getInstance()->getBool("LocalArt")); local_art->setState(Settings::getInstance()->getBool("LocalArt"));
s->addWithLabel("SEARCH FOR LOCAL ART", local_art); 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); auto launchstring_override = std::make_shared<SwitchComponent>(mWindow);
launchstring_override->setState(Settings::getInstance()->getBool("LaunchstringOverride")); launchstring_override->setState(Settings::getInstance()->getBool("LaunchstringOverride"));
s->addWithLabel("PER GAME OVERRIDE OF LAUNCH STRING", launchstring_override); s->addWithLabel("PER GAME LAUNCH STRING OVERRIDE", launchstring_override);
s->addSaveFunc([launchstring_override] { Settings::getInstance()->setBool("LaunchstringOverride", launchstring_override->getState()); }); s->addSaveFunc([launchstring_override] { Settings::getInstance()->
setBool("LaunchstringOverride", launchstring_override->getState()); });
// hidden files // Hidden files.
auto hidden_files = std::make_shared<SwitchComponent>(mWindow); auto hidden_files = std::make_shared<SwitchComponent>(mWindow);
hidden_files->setState(Settings::getInstance()->getBool("ShowHiddenFiles")); hidden_files->setState(Settings::getInstance()->getBool("ShowHiddenFiles"));
s->addWithLabel("SHOW HIDDEN FILES", hidden_files); 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_ #ifdef _RPI_
// Video Player - VideoOmxPlayer // Video Player - VideoOmxPlayer.
auto omx_player = std::make_shared<SwitchComponent>(mWindow); auto omx_player = std::make_shared<SwitchComponent>(mWindow);
omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer")); omx_player->setState(Settings::getInstance()->getBool("VideoOmxPlayer"));
s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player); s->addWithLabel("USE OMX PLAYER (HW ACCELERATED)", omx_player);
s->addSaveFunc([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; bool needReload = false;
if(Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState()) if (Settings::getInstance()->getBool("VideoOmxPlayer") != omx_player->getState())
needReload = true; needReload = true;
Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState()); Settings::getInstance()->setBool("VideoOmxPlayer", omx_player->getState());
if(needReload) if (needReload)
ViewController::get()->reloadAll(); ViewController::get()->reloadAll();
}); });
#endif #endif
// framerate // Framerate.
auto framerate = std::make_shared<SwitchComponent>(mWindow); auto framerate = std::make_shared<SwitchComponent>(mWindow);
framerate->setState(Settings::getInstance()->getBool("DrawFramerate")); framerate->setState(Settings::getInstance()->getBool("DrawFramerate"));
s->addWithLabel("SHOW FRAMERATE", framerate); 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); mWindow->pushGui(s);
@ -521,7 +586,6 @@ void GuiMenu::openConfigInput()
window->pushGui(new GuiDetectDevice(window, false, nullptr)); window->pushGui(new GuiDetectDevice(window, false, nullptr));
}, "NO", nullptr) }, "NO", nullptr)
); );
} }
void GuiMenu::openQuitMenu() void GuiMenu::openQuitMenu()
@ -531,10 +595,8 @@ void GuiMenu::openQuitMenu()
Window* window = mWindow; Window* window = mWindow;
ComponentListRow row; ComponentListRow row;
if (UIModeController::getInstance()->isUIModeFull()) if (UIModeController::getInstance()->isUIModeFull()) {
{ if (Settings::getInstance()->getBool("ShowExit")) {
if(Settings::getInstance()->getBool("ShowExit"))
{
row.makeAcceptInputHandler([window] { row.makeAcceptInputHandler([window] {
window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES", window->pushGui(new GuiMsgBox(window, "REALLY QUIT?", "YES",
[] { [] {
@ -542,13 +604,13 @@ void GuiMenu::openQuitMenu()
quitES(); quitES();
}, "NO", nullptr)); }, "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); s->addRow(row);
} }
} }
if(Settings::getInstance()->getBool("ShowRebootSystem")) if (Settings::getInstance()->getBool("ShowRebootSystem")) {
{
row.elements.clear(); row.elements.clear();
row.makeAcceptInputHandler([window] { row.makeAcceptInputHandler([window] {
window->pushGui(new GuiMsgBox(window, "REALLY REBOOT?", "YES", window->pushGui(new GuiMsgBox(window, "REALLY REBOOT?", "YES",
@ -559,12 +621,12 @@ void GuiMenu::openQuitMenu()
LOG(LogWarning) << "Reboot terminated with non-zero result!"; LOG(LogWarning) << "Reboot terminated with non-zero result!";
}, "NO", nullptr)); }, "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); s->addRow(row);
} }
if(Settings::getInstance()->getBool("ShowPoweroffSystem")) if (Settings::getInstance()->getBool("ShowPoweroffSystem")) {
{
row.elements.clear(); row.elements.clear();
row.makeAcceptInputHandler([window] { row.makeAcceptInputHandler([window] {
window->pushGui(new GuiMsgBox(window, "REALLY POWER OFF?", "YES", 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!"; LOG(LogWarning) << "Power off terminated with non-zero result!";
}, "NO", nullptr)); }, "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); s->addRow(row);
} }
@ -584,8 +647,6 @@ void GuiMenu::openQuitMenu()
void GuiMenu::addVersionInfo() 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.setFont(Font::get(FONT_SIZE_SMALL));
mVersion.setColor(0x5E5E5EFF); mVersion.setColor(0x5E5E5EFF);
mVersion.setText("EMULATIONSTATION-DE V" + Utils::String::toUpper(PROGRAM_VERSION_STRING)); 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()); 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); std::shared_ptr<Font> font = Font::get(FONT_SIZE_MEDIUM);
// populate the list // Populate the list.
ComponentListRow row; ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true); row.addElement(std::make_shared<TextComponent>(mWindow, name, font, color), true);
if(add_arrow) if (add_arrow) {
{
std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow); std::shared_ptr<ImageComponent> bracket = makeArrow(mWindow);
row.addElement(bracket, false); 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) bool GuiMenu::input(InputConfig* config, Input input)
{ {
if(GuiComponent::input(config, input)) if (GuiComponent::input(config, input))
return true; 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; delete this;
return true; return true;
} }

View file

@ -1,3 +1,9 @@
//
// GuiMenu.h
//
// Main menu.
//
#pragma once #pragma once
#ifndef ES_APP_GUIS_GUI_MENU_H #ifndef ES_APP_GUIS_GUI_MENU_H
#define ES_APP_GUIS_GUI_MENU_H #define ES_APP_GUIS_GUI_MENU_H
@ -16,7 +22,8 @@ public:
HelpStyle getHelpStyle() override; HelpStyle getHelpStyle() override;
private: 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 addVersionInfo();
void openCollectionSystemSettings(); void openCollectionSystemSettings();
void openConfigInput(); void openConfigInput();

View file

@ -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 "views/ViewController.h"
#include "animations/Animation.h" #include "animations/Animation.h"
@ -18,7 +27,7 @@
#include "Window.h" #include "Window.h"
#include "Sound.h" #include "Sound.h"
ViewController* ViewController::sInstance = NULL; ViewController* ViewController::sInstance = nullptr;
NavigationSounds navigationsounds; NavigationSounds navigationsounds;
ViewController* ViewController::get() ViewController* ViewController::get()
@ -33,8 +42,13 @@ void ViewController::init(Window* window)
sInstance = new ViewController(window); sInstance = new ViewController(window);
} }
ViewController::ViewController(Window* window) ViewController::ViewController(
: GuiComponent(window), mCurrentView(nullptr), mCamera(Transform4x4f::Identity()), mFadeOpacity(0), mLockInput(false) Window* window)
: GuiComponent(window),
mCurrentView(nullptr),
mCamera(Transform4x4f::Identity()),
mFadeOpacity(0),
mLockInput(false)
{ {
mState.viewing = NOTHING; mState.viewing = NOTHING;
} }
@ -42,24 +56,23 @@ ViewController::ViewController(Window* window)
ViewController::~ViewController() ViewController::~ViewController()
{ {
assert(sInstance == this); assert(sInstance == this);
sInstance = NULL; sInstance = nullptr;
} }
void ViewController::goToStart() 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"); auto requestedSystem = Settings::getInstance()->getString("StartupSystem");
if("" != requestedSystem && "retropie" != requestedSystem) if ("" != requestedSystem && "retropie" != requestedSystem) {
{ for (auto it = SystemData::sSystemVector.cbegin();
for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++){ it != SystemData::sSystemVector.cend(); it++) {
if ((*it)->getName() == requestedSystem) if ((*it)->getName() == requestedSystem) {
{
goToGameList(*it); goToGameList(*it);
return; return;
} }
} }
// Requested system doesn't exist // Requested system doesn't exist.
Settings::getInstance()->setString("StartupSystem", ""); Settings::getInstance()->setString("StartupSystem", "");
} }
goToSystemView(SystemData::sSystemVector.at(0)); goToSystemView(SystemData::sSystemVector.at(0));
@ -80,17 +93,16 @@ int ViewController::getSystemId(SystemData* system)
void ViewController::goToSystemView(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) if (mCurrentView)
{
mCurrentView->onHide(); mCurrentView->onHide();
}
mState.viewing = SYSTEM_SELECT; mState.viewing = SYSTEM_SELECT;
mState.system = system; mState.system = system;
auto systemList = getSystemListView(); 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); systemList->goToSystem(system, false);
mCurrentView = systemList; mCurrentView = systemList;
@ -120,13 +132,14 @@ void ViewController::goToPrevGameList()
void ViewController::goToGameList(SystemData* system) void ViewController::goToGameList(SystemData* system)
{ {
if(mState.viewing == SYSTEM_SELECT) if (mState.viewing == SYSTEM_SELECT) {
{ // Move system list.
// move system list
auto sysList = getSystemListView(); auto sysList = getSystemListView();
float offX = sysList->getPosition().x(); float offX = sysList->getPosition().x();
int sysId = getSystemId(system); 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; offX = sysList->getPosition().x() - offX;
mCamera.translation().x() -= offX; mCamera.translation().x() -= offX;
} }
@ -135,67 +148,65 @@ void ViewController::goToGameList(SystemData* system)
mState.system = system; mState.system = system;
if (mCurrentView) if (mCurrentView)
{
mCurrentView->onHide(); mCurrentView->onHide();
}
mCurrentView = getGameListView(system); mCurrentView = getGameListView(system);
if (mCurrentView) if (mCurrentView)
{
mCurrentView->onShow(); mCurrentView->onShow();
}
playViewTransition(); playViewTransition();
} }
void ViewController::playViewTransition() void ViewController::playViewTransition()
{ {
Vector3f target(Vector3f::Zero()); Vector3f target(Vector3f::Zero());
if(mCurrentView) if (mCurrentView)
target = mCurrentView->getPosition(); target = mCurrentView->getPosition();
// no need to animate, we're not going anywhere (probably goToNextGamelist() or goToPrevGamelist() when there's only 1 system) // No need to animate, we're not going anywhere (probably due to goToNextGamelist()
if(target == -mCamera.translation() && !isAnimationPlaying(0)) // or goToPrevGamelist() being called when there's only 1 system).
if (target == -mCamera.translation() && !isAnimationPlaying(0))
return; return;
std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
if(transition_style == "fade")
{ if (transition_style == "fade") {
// fade // Fade.
// stop whatever's currently playing, leaving mFadeOpacity wherever it is // Stop whatever's currently playing, leaving mFadeOpacity wherever it is.
cancelAnimation(0); cancelAnimation(0);
auto fadeFunc = [this](float t) { auto fadeFunc = [this](float t) {
mFadeOpacity = Math::lerp(0, 1, t); mFadeOpacity = Math::lerp(0, 1, t);
}; };
const static int FADE_DURATION = 240; // fade in/out time 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_WAIT = 320; // Time to wait between in/out.
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, [this, fadeFunc, target] { setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), 0, [this, fadeFunc, target] {
this->mCamera.translation() = -target; this->mCamera.translation() = -target;
updateHelpPrompts(); updateHelpPrompts();
setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT, nullptr, true); setAnimation(new LambdaAnimation(fadeFunc, FADE_DURATION), FADE_WAIT, nullptr, true);
}); });
// fast-forward animation if we're partway faded // Fast-forward animation if we're partway faded.
if(target == -mCamera.translation()) if (target == -mCamera.translation()) {
{ // Not changing screens, so cancel the first half entirely.
// not changing screens, so cancel the first half entirely
advanceAnimation(0, FADE_DURATION); advanceAnimation(0, FADE_DURATION);
advanceAnimation(0, FADE_WAIT); advanceAnimation(0, FADE_WAIT);
advanceAnimation(0, FADE_DURATION - (int)(mFadeOpacity * FADE_DURATION)); advanceAnimation(0, FADE_DURATION - (int)(mFadeOpacity * FADE_DURATION));
}else{ }
else {
advanceAnimation(0, (int)(mFadeOpacity * FADE_DURATION)); 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)); setAnimation(new MoveCameraAnimation(mCamera, target));
updateHelpPrompts(); // update help prompts immediately updateHelpPrompts(); // Update help prompts immediately.
} else { }
// instant else {
setAnimation(new LambdaAnimation( // Instant.
[this, target](float /*t*/) setAnimation(new LambdaAnimation([this, target](float /*t*/) {
{ this->mCamera.translation() = -target; }, 1));
this->mCamera.translation() = -target;
}, 1));
updateHelpPrompts(); updateHelpPrompts();
} }
} }
@ -203,19 +214,18 @@ void ViewController::playViewTransition()
void ViewController::onFileChanged(FileData* file, FileChangeType change) void ViewController::onFileChanged(FileData* file, FileChangeType change)
{ {
auto it = mGameListViews.find(file->getSystem()); auto it = mGameListViews.find(file->getSystem());
if(it != mGameListViews.cend()) if (it != mGameListViews.cend())
it->second->onFileChanged(file, change); it->second->onFileChanged(file, change);
} }
void ViewController::launch(FileData* game, Vector3f center) 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"; LOG(LogError) << "tried to launch something that isn't a game";
return; return;
} }
// Hide the current view // Hide the current view.
if (mCurrentView) if (mCurrentView)
mCurrentView->onHide(); mCurrentView->onHide();
@ -223,47 +233,51 @@ void ViewController::launch(FileData* game, Vector3f center)
origCamera.translation() = -mCurrentView->getPosition(); origCamera.translation() = -mCurrentView->getPosition();
center += mCurrentView->getPosition(); center += mCurrentView->getPosition();
stopAnimation(1); // make sure the fade in isn't still playing stopAnimation(1); // Make sure the fade in isn't still playing.
mWindow->stopInfoPopup(); // make sure we disable any existing info popup mWindow->stopInfoPopup(); // Make sure we disable any existing info popup.
mLockInput = true; mLockInput = true;
std::string transition_style = Settings::getInstance()->getString("TransitionStyle"); std::string transition_style = Settings::getInstance()->getString("TransitionStyle");
navigationsounds.playThemeNavigationSound(LAUNCHSOUND); 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)); while(navigationsounds.isPlayingThemeNavigationSound(LAUNCHSOUND));
if(transition_style == "fade") if (transition_style == "fade") {
{ // Fade out, launch game, fade back in.
// fade out, launch game, fade back in
auto fadeFunc = [this](float t) { auto fadeFunc = [this](float t) {
mFadeOpacity = Math::lerp(0.0f, 1.0f, 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); 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); this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView) if (mCurrentView)
mCurrentView->onShow(); mCurrentView->onShow();
}); });
} else if (transition_style == "slide"){ }
// move camera to zoom in on center + fade out, launch game, come back in else if (transition_style == "slide") {
setAnimation(new LaunchAnimation(mCamera, mFadeOpacity, center, 1500), 0, [this, origCamera, center, game] // 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); game->launchGame(mWindow);
mCamera = origCamera; 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); this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView) if (mCurrentView)
mCurrentView->onShow(); 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); game->launchGame(mWindow);
mCamera = origCamera; 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); this->onFileChanged(game, FILE_METADATA_CHANGED);
if (mCurrentView) if (mCurrentView)
mCurrentView->onShow(); mCurrentView->onShow();
@ -273,10 +287,8 @@ void ViewController::launch(FileData* game, Vector3f center)
void ViewController::removeGameListView(SystemData* system) void ViewController::removeGameListView(SystemData* system)
{ {
//if we already made one, return that one
auto exists = mGameListViews.find(system); auto exists = mGameListViews.find(system);
if(exists != mGameListViews.cend()) if (exists != mGameListViews.cend()) {
{
exists->second.reset(); exists->second.reset();
mGameListViews.erase(system); mGameListViews.erase(system);
} }
@ -284,18 +296,18 @@ void ViewController::removeGameListView(SystemData* system)
std::shared_ptr<IGameListView> ViewController::getGameListView(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); auto exists = mGameListViews.find(system);
if(exists != mGameListViews.cend()) if (exists != mGameListViews.cend())
return exists->second; return exists->second;
system->getIndex()->setUIModeFilters(); 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; std::shared_ptr<IGameListView> view;
bool themeHasVideoView = system->getTheme()->hasView("video"); bool themeHasVideoView = system->getTheme()->hasView("video");
//decide type // Decide type.
GameListViewType selectedViewType = AUTOMATIC; GameListViewType selectedViewType = AUTOMATIC;
std::string viewPreference = Settings::getInstance()->getString("GamelistViewStyle"); std::string viewPreference = Settings::getInstance()->getString("GamelistViewStyle");
@ -308,39 +320,39 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
if (viewPreference.compare("video") == 0) if (viewPreference.compare("video") == 0)
selectedViewType = VIDEO; selectedViewType = VIDEO;
if (selectedViewType == AUTOMATIC) if (selectedViewType == AUTOMATIC) {
{
std::vector<FileData*> files = system->getRootFolder()->getFilesRecursive(GAME | FOLDER); std::vector<FileData*> files = system->getRootFolder()->getFilesRecursive(GAME | FOLDER);
for (auto it = files.cbegin(); it != files.cend(); it++) for (auto it = files.cbegin(); it != files.cend(); it++) {
{ if (themeHasVideoView && !(*it)->getVideoPath().empty()) {
if (themeHasVideoView && !(*it)->getVideoPath().empty())
{
selectedViewType = VIDEO; selectedViewType = VIDEO;
break; break;
} }
else if (!(*it)->getThumbnailPath().empty()) else if (!(*it)->getThumbnailPath().empty()) {
{
selectedViewType = DETAILED; 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) switch (selectedViewType)
{ {
case VIDEO: case VIDEO:
view = std::shared_ptr<IGameListView>(new VideoGameListView(mWindow, system->getRootFolder())); view = std::shared_ptr<IGameListView>(
new VideoGameListView(mWindow, system->getRootFolder()));
break; break;
case DETAILED: case DETAILED:
view = std::shared_ptr<IGameListView>(new DetailedGameListView(mWindow, system->getRootFolder())); view = std::shared_ptr<IGameListView>(
new DetailedGameListView(mWindow, system->getRootFolder()));
break; break;
case GRID: case GRID:
view = std::shared_ptr<IGameListView>(new GridGameListView(mWindow, system->getRootFolder())); view = std::shared_ptr<IGameListView>(
new GridGameListView(mWindow, system->getRootFolder()));
break; break;
case BASIC: case BASIC:
default: default:
view = std::shared_ptr<IGameListView>(new BasicGameListView(mWindow, system->getRootFolder())); view = std::shared_ptr<IGameListView>(
new BasicGameListView(mWindow, system->getRootFolder()));
break; break;
} }
@ -348,7 +360,8 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
std::vector<SystemData*>& sysVec = SystemData::sSystemVector; std::vector<SystemData*>& sysVec = SystemData::sSystemVector;
int id = (int)(std::find(sysVec.cbegin(), sysVec.cend(), system) - sysVec.cbegin()); 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()); addChild(view.get());
@ -358,8 +371,8 @@ std::shared_ptr<IGameListView> ViewController::getGameListView(SystemData* syste
std::shared_ptr<SystemView> ViewController::getSystemListView() std::shared_ptr<SystemView> ViewController::getSystemListView()
{ {
//if we already made one, return that one // If we already made one, return that one.
if(mSystemListView) if (mSystemListView)
return mSystemListView; return mSystemListView;
mSystemListView = std::shared_ptr<SystemView>(new SystemView(mWindow)); 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) bool ViewController::input(InputConfig* config, Input input)
{ {
if(mLockInput) if (mLockInput)
return true; return true;
// open menu // Open menu.
if(!(UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("DisableKidStartMenu")) && config->isMappedTo("start", input) && input.value != 0) if (!(UIModeController::getInstance()->isUIModeKid() &&
{ Settings::getInstance()->getBool("DisableKidStartMenu")) &&
// open menu config->isMappedTo("start", input) && input.value != 0) {
mWindow->pushGui(new GuiMenu(mWindow)); mWindow->pushGui(new GuiMenu(mWindow));
return true; 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; return true;
}
if(mCurrentView) if (mCurrentView)
return mCurrentView->input(config, input); return mCurrentView->input(config, input);
return false; return false;
@ -395,10 +407,8 @@ bool ViewController::input(InputConfig* config, Input input)
void ViewController::update(int deltaTime) void ViewController::update(int deltaTime)
{ {
if(mCurrentView) if (mCurrentView)
{
mCurrentView->update(deltaTime); mCurrentView->update(deltaTime);
}
updateSelf(deltaTime); updateSelf(deltaTime);
} }
@ -409,48 +419,48 @@ void ViewController::render(const Transform4x4f& parentTrans)
Transform4x4f transInverse; Transform4x4f transInverse;
transInverse.invert(trans); transInverse.invert(trans);
// camera position, position + size // Camera position, position + size.
Vector3f viewStart = transInverse.translation(); 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. // Keep track of UI mode changes.
UIModeController::getInstance()->monitorUIMode(); UIModeController::getInstance()->monitorUIMode();
// draw systemview // Draw system view.
getSystemListView()->render(trans); getSystemListView()->render(trans);
// draw gamelists // Draw gamelists.
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) {
{ // Clipping.
// clipping
Vector3f guiStart = it->second->getPosition(); 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() && if (guiEnd.x() >= viewStart.x() && guiEnd.y() >= viewStart.y() &&
guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y()) guiStart.x() <= viewEnd.x() && guiStart.y() <= viewEnd.y())
it->second->render(trans); it->second->render(trans);
} }
if(mWindow->peekGui() == this) if (mWindow->peekGui() == this)
mWindow->renderHelpPromptsEarly(); mWindow->renderHelpPromptsEarly();
// fade out // Fade out.
if(mFadeOpacity) if (mFadeOpacity) {
{
unsigned int fadeColor = 0x00000000 | (unsigned char)(mFadeOpacity * 255); unsigned int fadeColor = 0x00000000 | (unsigned char)(mFadeOpacity * 255);
Renderer::setMatrix(parentTrans); 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() void ViewController::preload()
{ {
uint32_t i = 0; uint32_t i = 0;
for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++) for (auto it = SystemData::sSystemVector.cbegin();
{ it != SystemData::sSystemVector.cend(); it++) {
if(Settings::getInstance()->getBool("SplashScreen") && if (Settings::getInstance()->getBool("SplashScreen") &&
Settings::getInstance()->getBool("SplashScreenProgress")) Settings::getInstance()->getBool("SplashScreenProgress")) {
{
i++; i++;
char buffer[100]; char buffer[100];
sprintf (buffer, "Loading '%s' (%d/%d)", sprintf (buffer, "Loading '%s' (%d/%d)",
@ -461,80 +471,74 @@ void ViewController::preload()
(*it)->getIndex()->resetFilters(); (*it)->getIndex()->resetFilters();
getGameListView(*it); getGameListView(*it);
} }
// load navigation sounds // Load navigation sounds.
navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme()); navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme());
} }
void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme) void ViewController::reloadGameListView(IGameListView* view, bool reloadTheme)
{ {
for(auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) for (auto it = mGameListViews.cbegin(); it != mGameListViews.cend(); it++) {
{ if (it->second.get() == view) {
if(it->second.get() == view)
{
bool isCurrent = (mCurrentView == it->second); bool isCurrent = (mCurrentView == it->second);
SystemData* system = it->first; SystemData* system = it->first;
FileData* cursor = view->getCursor(); FileData* cursor = view->getCursor();
mGameListViews.erase(it); mGameListViews.erase(it);
if(reloadTheme) if (reloadTheme)
system->loadTheme(); system->loadTheme();
system->getIndex()->setUIModeFilters(); system->getIndex()->setUIModeFilters();
std::shared_ptr<IGameListView> newView = getGameListView(system); std::shared_ptr<IGameListView> newView = getGameListView(system);
// to counter having come from a placeholder // To counter having come from a placeholder.
if (!cursor->isPlaceHolder()) { if (!cursor->isPlaceHolder()) {
newView->setCursor(cursor); newView->setCursor(cursor);
} }
if(isCurrent) if (isCurrent)
mCurrentView = newView; mCurrentView = newView;
break; break;
} }
} }
// Redisplay the current view // Redisplay the current view.
if (mCurrentView) if (mCurrentView)
mCurrentView->onShow(); mCurrentView->onShow();
} }
void ViewController::reloadAll() void ViewController::reloadAll()
{ {
// clear all gamelistviews // Clear all GameListViews.
std::map<SystemData*, FileData*> cursorMap; 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(); cursorMap[it->first] = it->second->getCursor();
}
mGameListViews.clear(); mGameListViews.clear();
// Load themes, create GameListViews and reset filters.
// load themes, create gamelistviews and reset filters for (auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++) {
for(auto it = cursorMap.cbegin(); it != cursorMap.cend(); it++)
{
it->first->loadTheme(); it->first->loadTheme();
it->first->getIndex()->resetFilters(); it->first->getIndex()->resetFilters();
getGameListView(it->first)->setCursor(it->second); getGameListView(it->first)->setCursor(it->second);
} }
// Rebuild SystemListView // Rebuild SystemListView.
mSystemListView.reset(); mSystemListView.reset();
getSystemListView(); getSystemListView();
// update mCurrentView since the pointers changed // Update mCurrentView since the pointers changed.
if(mState.viewing == GAME_LIST) if (mState.viewing == GAME_LIST) {
{
mCurrentView = getGameListView(mState.getSystem()); mCurrentView = getGameListView(mState.getSystem());
}else if(mState.viewing == SYSTEM_SELECT) }
{ else if (mState.viewing == SYSTEM_SELECT) {
SystemData* system = mState.getSystem(); SystemData* system = mState.getSystem();
goToSystemView(SystemData::sSystemVector.front()); goToSystemView(SystemData::sSystemVector.front());
mSystemListView->goToSystem(system, false); mSystemListView->goToSystem(system, false);
mCurrentView = mSystemListView; mCurrentView = mSystemListView;
}else{ }
else {
goToSystemView(SystemData::sSystemVector.front()); goToSystemView(SystemData::sSystemVector.front());
} }
// load navigation sounds // Load navigation sounds.
navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme()); navigationsounds.loadThemeNavigationSounds(SystemData::sSystemVector[0]->getTheme());
updateHelpPrompts(); updateHelpPrompts();
@ -543,19 +547,19 @@ void ViewController::reloadAll()
std::vector<HelpPrompt> ViewController::getHelpPrompts() std::vector<HelpPrompt> ViewController::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;
if(!mCurrentView) if (!mCurrentView)
return prompts; return prompts;
prompts = mCurrentView->getHelpPrompts(); 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")); prompts.push_back(HelpPrompt("start", "menu"));
return prompts; return prompts;
} }
HelpStyle ViewController::getHelpStyle() HelpStyle ViewController::getHelpStyle()
{ {
if(!mCurrentView) if (!mCurrentView)
return GuiComponent::getHelpStyle(); return GuiComponent::getHelpStyle();
return mCurrentView->getHelpStyle(); return mCurrentView->getHelpStyle();

View file

@ -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 #pragma once
#ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H #ifndef ES_APP_VIEWS_VIEW_CONTROLLER_H
#define ES_APP_VIEWS_VIEW_CONTROLLER_H #define ES_APP_VIEWS_VIEW_CONTROLLER_H
@ -11,7 +20,8 @@ class IGameListView;
class SystemData; class SystemData;
class SystemView; 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 class ViewController : public GuiComponent
{ {
public: public:
@ -27,8 +37,11 @@ public:
// If a basic view detected a metadata change, it can request to recreate // If a basic view detected a metadata change, it can request to recreate
// the current gamelist view (as it may change to be detailed). // the current gamelist view (as it may change to be detailed).
void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false); void reloadGameListView(IGameListView* gamelist, bool reloadTheme = false);
inline void reloadGameListView(SystemData* system, bool reloadTheme = false) { reloadGameListView(getGameListView(system).get(), reloadTheme); } inline void reloadGameListView(SystemData* system, bool reloadTheme = false)
void reloadAll(); // Reload everything with a theme. Used when the "ThemeSet" setting changes. { reloadGameListView(getGameListView(system).get(), reloadTheme); }
// Reload everything with a theme.
// Used when the "ThemeSet" setting changes.
void reloadAll();
// Navigation. // Navigation.
void goToNextGameList(); void goToNextGameList();
@ -42,22 +55,21 @@ public:
// Plays a nice launch effect and launches the game at the end of it. // Plays a nice launch effect and launches the game at the end of it.
// Once the game terminates, plays a return effect. // 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; bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override; void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override; void render(const Transform4x4f& parentTrans) override;
enum ViewMode enum ViewMode {
{
NOTHING, NOTHING,
START_SCREEN, START_SCREEN,
SYSTEM_SELECT, SYSTEM_SELECT,
GAME_LIST GAME_LIST
}; };
enum GameListViewType enum GameListViewType {
{
AUTOMATIC, AUTOMATIC,
BASIC, BASIC,
DETAILED, DETAILED,
@ -65,11 +77,11 @@ public:
VIDEO VIDEO
}; };
struct State struct State {
{
ViewMode viewing; 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: private:
friend ViewController; friend ViewController;

View file

@ -1,3 +1,9 @@
//
// BasicGameListView.cpp
//
// Interface that defines a GameListView of the type 'Basic'.
//
#include "views/gamelist/BasicGameListView.h" #include "views/gamelist/BasicGameListView.h"
#include "utils/FileSystemUtil.h" #include "utils/FileSystemUtil.h"
@ -7,8 +13,11 @@
#include "Settings.h" #include "Settings.h"
#include "SystemData.h" #include "SystemData.h"
BasicGameListView::BasicGameListView(Window* window, FileData* root) BasicGameListView::BasicGameListView(
: ISimpleGameListView(window, root), mList(window) Window* window,
FileData* root)
: ISimpleGameListView(window, root),
mList(window)
{ {
mList.setSize(mSize.x(), mSize.y() * 0.8f); mList.setSize(mSize.x(), mSize.y() * 0.8f);
mList.setPosition(0, mSize.y() * 0.2f); 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) 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); ViewController::get()->reloadGameListView(this);
return; return;
} }
@ -43,55 +52,23 @@ void BasicGameListView::populateList(const std::vector<FileData*>& files)
{ {
mList.clear(); mList.clear();
mHeaderText.setText(mRoot->getSystem()->getFullName()); mHeaderText.setText(mRoot->getSystem()->getFullName());
if (files.size() > 0) if (files.size() > 0) {
{ for (auto it = files.cbegin(); it != files.cend(); it++) {
if ((*it)->getFavorite() &&
std::string systemName = mRoot->getSystem()->getName(); mRoot->getSystem()->getName() != "favorites") {
mList.add(FAVORITE_GAME_CHAR + " " + (*it)->getName(),
bool favoritesFirst = Settings::getInstance()->getBool("FavoritesFirst"); *it, ((*it)->getType() == FOLDER));
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);
} }
} else if ((*it)->getType() == FOLDER &&
mRoot->getSystem()->getName() != "collections") {
for (auto file : files) mList.add(FAVORITE_FOLDER_CHAR + " " + (*it)->getName(), *it, true);
{ }
if (file->getFavorite()) else {
{ mList.add((*it)->getName(), *it, ((*it)->getType() == FOLDER));
if (favoritesFirst)
continue;
if (showFavoriteIcon)
{
mList.add(FAVORITE_GAME_CHAR + " " + file->getName(), file, file->getType() == FOLDER);
continue;
}
} }
if (file->getType() == FOLDER)
mList.add(FAVORITE_FOLDER_CHAR + " " + file->getName(), file, true);
else
mList.add(file->getName(), file, false);
} }
} }
else else {
{
addPlaceholder(); addPlaceholder();
} }
} }
@ -103,25 +80,25 @@ FileData* BasicGameListView::getCursor()
void BasicGameListView::setCursor(FileData* cursor) void BasicGameListView::setCursor(FileData* cursor)
{ {
if(!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) if (!mList.setCursor(cursor) && (!cursor->isPlaceHolder())) {
{
populateList(cursor->getParent()->getChildrenListToDisplay()); populateList(cursor->getParent()->getChildrenListToDisplay());
mList.setCursor(cursor); mList.setCursor(cursor);
// update our cursor stack in case our cursor just got set to some folder we weren't in before // Update our cursor stack in case our cursor just
if(mCursorStack.empty() || mCursorStack.top() != cursor->getParent()) // got set to some folder we weren't in before.
if (mCursorStack.empty() || mCursorStack.top() != cursor->getParent())
{ {
std::stack<FileData*> tmp; std::stack<FileData*> tmp;
FileData* ptr = cursor->getParent(); FileData* ptr = cursor->getParent();
while(ptr && ptr != mRoot) while (ptr && ptr != mRoot)
{ {
tmp.push(ptr); tmp.push(ptr);
ptr = ptr->getParent(); ptr = ptr->getParent();
} }
// flip the stack and put it in mCursorStack // Flip the stack and put it in mCursorStack.
mCursorStack = std::stack<FileData*>(); mCursorStack = std::stack<FileData*>();
while(!tmp.empty()) while (!tmp.empty())
{ {
mCursorStack.push(tmp.top()); mCursorStack.push(tmp.top());
tmp.pop(); tmp.pop();
@ -132,8 +109,9 @@ void BasicGameListView::setCursor(FileData* cursor)
void BasicGameListView::addPlaceholder() void BasicGameListView::addPlaceholder()
{ {
// empty list - add a placeholder // Empty list - add a placeholder.
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>",
this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER)); mList.add(placeholder->getName(), placeholder, (placeholder->getType() == PLACEHOLDER));
} }
@ -154,47 +132,47 @@ void BasicGameListView::launch(FileData* game)
void BasicGameListView::remove(FileData *game, bool deleteFile) void BasicGameListView::remove(FileData *game, bool deleteFile)
{ {
// Actually delete the file on the filesystem.
if (deleteFile) if (deleteFile)
Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem Utils::FileSystem::removeFile(game->getPath());
FileData* parent = game->getParent(); 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(); std::vector<FileData*> siblings = parent->getChildrenListToDisplay();
auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game); auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game);
unsigned int gamePos = (int)std::distance(siblings.cbegin(), gameIter); unsigned int gamePos = (int)std::distance(siblings.cbegin(), gameIter);
if (gameIter != siblings.cend()) if (gameIter != siblings.cend()) {
{
if ((gamePos + 1) < siblings.size()) if ((gamePos + 1) < siblings.size())
{
setCursor(siblings.at(gamePos + 1)); setCursor(siblings.at(gamePos + 1));
} else if (gamePos > 1) { else if (gamePos > 1)
setCursor(siblings.at(gamePos - 1)); setCursor(siblings.at(gamePos - 1));
}
} }
} }
mList.remove(game); mList.remove(game);
if(mList.size() == 0)
{ if (mList.size() == 0)
addPlaceholder(); addPlaceholder();
}
delete game; // remove before repopulating (removes from parent) // Remove before repopulating (removes from parent), then update the view.
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed delete game;
onFileChanged(parent, FILE_REMOVED);
} }
std::vector<HelpPrompt> BasicGameListView::getHelpPrompts() std::vector<HelpPrompt> BasicGameListView::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; 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("left/right", "system"));
prompts.push_back(HelpPrompt("up/down", "choose")); prompts.push_back(HelpPrompt("up/down", "choose"));
prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("a", "launch"));
prompts.push_back(HelpPrompt("b", "back")); prompts.push_back(HelpPrompt("b", "back"));
if(!UIModeController::getInstance()->isUIModeKid()) if (!UIModeController::getInstance()->isUIModeKid())
prompts.push_back(HelpPrompt("select", "options")); prompts.push_back(HelpPrompt("select", "options"));
if(mRoot->getSystem()->isGameSystem()) if (mRoot->getSystem()->isGameSystem())
prompts.push_back(HelpPrompt("x", "random")); 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(); std::string prompt = CollectionSystemManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));

View file

@ -1,3 +1,9 @@
//
// BasicGameListView.h
//
// Interface that defines a GameListView of the type 'basic'.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_BASIC_GAME_LIST_VIEW_H
#define 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: public:
BasicGameListView(Window* window, FileData* root); 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 onFileChanged(FileData* file, FileChangeType change);
virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme); virtual void onThemeChanged(const std::shared_ptr<ThemeData>& theme);

View file

@ -1,24 +1,44 @@
//
// DetailedGameListView.cpp
//
// Interface that defines a GameListView of the type 'detailed'.
//
#include "views/gamelist/DetailedGameListView.h" #include "views/gamelist/DetailedGameListView.h"
#include "animations/LambdaAnimation.h" #include "animations/LambdaAnimation.h"
#include "views/ViewController.h" #include "views/ViewController.h"
DetailedGameListView::DetailedGameListView(Window* window, FileData* root) : DetailedGameListView::DetailedGameListView(
BasicGameListView(window, root), Window* window,
mDescContainer(window), mDescription(window), FileData* root)
mThumbnail(window), : BasicGameListView(window, root),
mMarquee(window), mDescContainer(window),
mImage(window), mDescription(window),
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), mThumbnail(window),
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), mMarquee(window),
mImage(window),
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), mLblRating(window),
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), mLblReleaseDate(window),
mName(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; const float padding = 0.01f;
mList.setPosition(mSize.x() * (0.50f + padding), mList.getPosition().y()); 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.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
// Thumbnail // Thumbnail.
mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setOrigin(0.5f, 0.5f);
mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setPosition(2.0f, 2.0f);
mThumbnail.setVisible(false); mThumbnail.setVisible(false);
@ -34,23 +54,23 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
mThumbnail.setDefaultZIndex(25); mThumbnail.setDefaultZIndex(25);
addChild(&mThumbnail); addChild(&mThumbnail);
// Marquee // Marquee.
mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setOrigin(0.5f, 0.5f);
// Default to off the screen // Default to off the screen.
mMarquee.setPosition(2.0f, 2.0f); mMarquee.setPosition(2.0f, 2.0f);
mMarquee.setVisible(false); mMarquee.setVisible(false);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35); mMarquee.setDefaultZIndex(35);
addChild(&mMarquee); addChild(&mMarquee);
// image // Image.
mImage.setOrigin(0.5f, 0.5f); mImage.setOrigin(0.5f, 0.5f);
mImage.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f); 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.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f);
mImage.setDefaultZIndex(30); mImage.setDefaultZIndex(30);
addChild(&mImage); addChild(&mImage);
// metadata labels + values // Metadata labels + values.
mLblRating.setText("Rating: "); mLblRating.setText("Rating: ");
addChild(&mLblRating); addChild(&mLblRating);
addChild(&mRating); addChild(&mRating);
@ -85,7 +105,8 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); 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.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -94,7 +115,6 @@ DetailedGameListView::DetailedGameListView(Window* window, FileData* root) :
mDescription.setSize(mDescContainer.getSize().x(), 0); mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescContainer.addChild(&mDescription); mDescContainer.addChild(&mDescription);
initMDLabels(); initMDLabels();
initMDValues(); initMDValues();
updateInfoPanel(); updateInfoPanel();
@ -105,9 +125,12 @@ void DetailedGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& them
BasicGameListView::onThemeChanged(theme); BasicGameListView::onThemeChanged(theme);
using namespace ThemeFlags; using namespace ThemeFlags;
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mImage.applyTheme(theme, getName(), "md_image", 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); mName.applyTheme(theme, getName(), "md_name", ALL);
initMDLabels(); 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" "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); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
}
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); 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" "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); 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.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(); sortChildren();
} }
@ -156,15 +176,14 @@ void DetailedGameListView::initMDLabels()
const float colSize = (mSize.x() * 0.48f) / colCount; const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y(); 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; const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f); Vector3f pos(0.0f, 0.0f, 0.0f);
if(row == 0) if (row == 0) {
{
pos = start + Vector3f(colSize * (i / rowCount), 0, 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]; GuiComponent* lc = components[i-1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -193,20 +212,22 @@ void DetailedGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; 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; 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]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
if(testBot > bottom)
if (testBot > bottom)
bottom = testBot; bottom = testBot;
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); 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() void DetailedGameListView::updateInfoPanel()
@ -214,12 +235,12 @@ void DetailedGameListView::updateInfoPanel()
FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected();
bool fadingOut; bool fadingOut;
if(file == NULL) if (file == nullptr) {
{
//mImage.setImage(""); //mImage.setImage("");
//mDescription.setText(""); //mDescription.setText("");
fadingOut = true; fadingOut = true;
}else{ }
else {
mThumbnail.setImage(file->getThumbnailPath()); mThumbnail.setImage(file->getThumbnailPath());
mMarquee.setImage(file->getMarqueePath()); mMarquee.setImage(file->getMarqueePath());
mImage.setImage(file->getImagePath()); mImage.setImage(file->getImagePath());
@ -234,8 +255,7 @@ void DetailedGameListView::updateInfoPanel()
mPlayers.setValue(file->metadata.get("players")); mPlayers.setValue(file->metadata.get("players"));
mName.setValue(file->metadata.get("name")); mName.setValue(file->metadata.get("name"));
if(file->getType() == GAME) if (file->getType() == GAME) {
{
mLastPlayed.setValue(file->metadata.get("lastplayed")); mLastPlayed.setValue(file->metadata.get("lastplayed"));
mPlayCount.setValue(file->metadata.get("playcount")); mPlayCount.setValue(file->metadata.get("playcount"));
} }
@ -252,18 +272,13 @@ void DetailedGameListView::updateInfoPanel()
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
comps.insert(comps.cend(), labels.cbegin(), labels.cend()); 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; GuiComponent* comp = *it;
// an animation is playing // An animation is playing, then animate if reverse != fadingOut
// then animate if reverse != fadingOut // An animation is not playing, then animate if opacity != our target opacity
// an animation is not playing if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
// then animate if opacity != our target opacity (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || auto func = [comp](float t) {
(!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->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
}; };
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
@ -274,7 +289,7 @@ void DetailedGameListView::updateInfoPanel()
void DetailedGameListView::launch(FileData* game) void DetailedGameListView::launch(FileData* game)
{ {
Vector3f target(Renderer::getScreenWidth() / 2.0f, Renderer::getScreenHeight() / 2.0f, 0); 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); target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
ViewController::get()->launch(game, target); ViewController::get()->launch(game, target);

View file

@ -1,3 +1,9 @@
//
// DetailedGameListView.h
//
// Interface that defines a GameListView of the type 'detailed'.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_DETAILED_GAME_LIST_VIEW_H
#define 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 mMarquee;
ImageComponent mImage; 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; RatingComponent mRating;
DateTimeComponent mReleaseDate; DateTimeComponent mReleaseDate;

View file

@ -1,3 +1,9 @@
//
// GridGameListView.cpp
//
// Interface that defines a GameListView of the type 'grid'.
//
#include "views/gamelist/GridGameListView.h" #include "views/gamelist/GridGameListView.h"
#include "animations/LambdaAnimation.h" #include "animations/LambdaAnimation.h"
@ -12,24 +18,42 @@
#endif #endif
#include "components/VideoVlcComponent.h" #include "components/VideoVlcComponent.h"
GridGameListView::GridGameListView(Window* window, FileData* root) : GridGameListView::GridGameListView(
ISimpleGameListView(window, root), Window* window,
mGrid(window), mMarquee(window), FileData* root)
mImage(window), : ISimpleGameListView(window, root),
mVideo(nullptr),
mVideoPlaying(false),
mDescContainer(window), mDescription(window),
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), mGrid(window),
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), mMarquee(window),
mImage(window),
mVideo(nullptr),
mVideoPlaying(false),
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), mDescContainer(window),
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), mDescription(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; const float padding = 0.01f;
// Create the correct type of video window // Create the correct type of video window.
#ifdef _RPI_ #ifdef _RPI_
if (Settings::getInstance()->getBool("VideoOmxPlayer")) if (Settings::getInstance()->getBool("VideoOmxPlayer"))
mVideo = new VideoPlayerComponent(window, ""); mVideo = new VideoPlayerComponent(window, "");
@ -46,7 +70,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
populateList(root->getChildrenListToDisplay()); populateList(root->getChildrenListToDisplay());
// metadata labels + values // Metadata labels + values.
mLblRating.setText("Rating: "); mLblRating.setText("Rating: ");
addChild(&mLblRating); addChild(&mLblRating);
addChild(&mRating); addChild(&mRating);
@ -81,7 +105,8 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); 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.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -89,7 +114,7 @@ GridGameListView::GridGameListView(Window* window, FileData* root) :
mDescription.setFont(Font::get(FONT_SIZE_SMALL)); mDescription.setFont(Font::get(FONT_SIZE_SMALL));
mDescription.setSize(mDescContainer.getSize().x(), 0); mDescription.setSize(mDescContainer.getSize().x(), 0);
mDescContainer.addChild(&mDescription); mDescContainer.addChild(&mDescription);
mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
@ -128,7 +153,7 @@ FileData* GridGameListView::getCursor()
void GridGameListView::setCursor(FileData* file) void GridGameListView::setCursor(FileData* file)
{ {
if(!mGrid.setCursor(file)) if (!mGrid.setCursor(file))
{ {
populateList(file->getParent()->getChildrenListToDisplay()); populateList(file->getParent()->getChildrenListToDisplay());
mGrid.setCursor(file); mGrid.setCursor(file);
@ -147,11 +172,13 @@ std::string GridGameListView::getQuickSystemSelectLeftButton()
bool GridGameListView::input(InputConfig* config, Input input) bool GridGameListView::input(InputConfig* config, Input input)
{ {
if (input.value == 0 and (config->isMappedLike("left", input) || config->isMappedLike("right", input) if (input.value == 0 and (config->isMappedLike("left", input) ||
|| (config->isMappedLike("up", input)) || (config->isMappedLike("down", input)) )) config->isMappedLike("right", input) ||
(config->isMappedLike("up", input)) ||
(config->isMappedLike("down", input)) ))
navigationsounds.playThemeNavigationSound(SCROLLSOUND); 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 GuiComponent::input(config, input);
return ISimpleGameListView::input(config, input); return ISimpleGameListView::input(config, input);
@ -167,8 +194,8 @@ const std::string GridGameListView::getImagePath(FileData* file)
else if (src == ImageSource::MARQUEE) else if (src == ImageSource::MARQUEE)
return file->getMarqueePath(); return file->getMarqueePath();
// If no thumbnail was found, then use the image media type // If no thumbnail was found, then use the image media type.
if(file->getThumbnailPath() == ""); if (file->getThumbnailPath() == "");
return file->getImagePath(); return file->getImagePath();
return file->getThumbnailPath(); return file->getThumbnailPath();
@ -178,17 +205,12 @@ void GridGameListView::populateList(const std::vector<FileData*>& files)
{ {
mGrid.clear(); mGrid.clear();
mHeaderText.setText(mRoot->getSystem()->getFullName()); mHeaderText.setText(mRoot->getSystem()->getFullName());
if (files.size() > 0) if (files.size() > 0) {
{
for (auto it = files.cbegin(); it != files.cend(); it++) for (auto it = files.cbegin(); it != files.cend(); it++)
{
mGrid.add((*it)->getName(), getImagePath(*it), *it); mGrid.add((*it)->getName(), getImagePath(*it), *it);
}
} }
else else
{
addPlaceholder(); addPlaceholder();
}
} }
void GridGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme) 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); mGrid.applyTheme(theme, getName(), "gamegrid", ALL);
mName.applyTheme(theme, getName(), "md_name", ALL); mName.applyTheme(theme, getName(), "md_name", ALL);
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mMarquee.applyTheme(theme, getName(), "md_marquee",
mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | 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(); initMDLabels();
std::vector<TextComponent*> labels = getMDLabels(); 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" "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); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
}
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); 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" "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); 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.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(); FileData* file = mGrid.getSelected();
populateList(mRoot->getChildrenListToDisplay()); populateList(mRoot->getChildrenListToDisplay());
mGrid.setCursor(file); mGrid.setCursor(file);
@ -254,15 +277,14 @@ void GridGameListView::initMDLabels()
const float colSize = (mSize.x() * 0.48f) / colCount; const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y(); 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; const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f); Vector3f pos(0.0f, 0.0f, 0.0f);
if(row == 0) if (row == 0) {
{
pos = start + Vector3f(colSize * (i / rowCount), 0, 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]; GuiComponent* lc = components[i-1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -291,36 +313,37 @@ void GridGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; 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; 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]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
if(testBot > bottom) if (testBot > bottom)
bottom = testBot; bottom = testBot;
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); 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() 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; bool fadingOut;
if(file == NULL) if (file == nullptr)
{ {
mVideo->setVideo(""); mVideo->setVideo("");
mVideo->setImage(""); mVideo->setImage("");
mVideoPlaying = false; mVideoPlaying = false;
//mDescription.setText("");
fadingOut = true; fadingOut = true;
}else{ }else{
// Temporary fix to disable only audio from playing
// if (!mVideo->setVideo(file->getVideoPath())) // if (!mVideo->setVideo(file->getVideoPath()))
// { // {
// mVideo->setDefaultVideo(); // mVideo->setDefaultVideo();
@ -330,7 +353,7 @@ void GridGameListView::updateInfoPanel()
// mVideo->setImage(file->getThumbnailPath()); // mVideo->setImage(file->getThumbnailPath());
mMarquee.setImage(file->getMarqueePath()); mMarquee.setImage(file->getMarqueePath());
// mImage.setImage(file->getImagePath()); // mImage.setImage(file->getImagePath());
mDescription.setText(file->metadata.get("desc")); mDescription.setText(file->metadata.get("desc"));
mDescContainer.reset(); mDescContainer.reset();
@ -342,8 +365,7 @@ void GridGameListView::updateInfoPanel()
mPlayers.setValue(file->metadata.get("players")); mPlayers.setValue(file->metadata.get("players"));
mName.setValue(file->metadata.get("name")); mName.setValue(file->metadata.get("name"));
if(file->getType() == GAME) if (file->getType() == GAME) {
{
mLastPlayed.setValue(file->metadata.get("lastplayed")); mLastPlayed.setValue(file->metadata.get("lastplayed"));
mPlayCount.setValue(file->metadata.get("playcount")); mPlayCount.setValue(file->metadata.get("playcount"));
} }
@ -360,18 +382,13 @@ void GridGameListView::updateInfoPanel()
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
comps.insert(comps.cend(), labels.cbegin(), labels.cend()); 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; GuiComponent* comp = *it;
// an animation is playing // An animation is playing, then animate if reverse != fadingOut
// then animate if reverse != fadingOut // An animation is not playing, then animate if opacity != our target opacity
// an animation is not playing if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
// then animate if opacity != our target opacity (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || auto func = [comp](float t) {
(!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->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
}; };
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut);
@ -381,67 +398,62 @@ void GridGameListView::updateInfoPanel()
void GridGameListView::addPlaceholder() void GridGameListView::addPlaceholder()
{ {
// empty grid - add a placeholder // Empty grid - add a placeholder.
FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>", this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem()); FileData* placeholder = new FileData(PLACEHOLDER, "<No Entries Found>",
this->mRoot->getSystem()->getSystemEnvData(), this->mRoot->getSystem());
mGrid.add(placeholder->getName(), "", placeholder); mGrid.add(placeholder->getName(), "", placeholder);
} }
void GridGameListView::launch(FileData* game) void GridGameListView::launch(FileData* game)
{ {
float screenWidth = (float) Renderer::getScreenWidth(); float screenWidth = (float) Renderer::getScreenWidth();
float screenHeight = (float) Renderer::getScreenHeight(); float screenHeight = (float) Renderer::getScreenHeight();
Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0); Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0);
if(mMarquee.hasImage() && if (mMarquee.hasImage() &&
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
{
target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0); target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0);
} else if (mImage.hasImage() &&
else if(mImage.hasImage() && (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
{
target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
} else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
{
target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0); target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0);
}
ViewController::get()->launch(game, target); ViewController::get()->launch(game, target);
} }
void GridGameListView::remove(FileData *game, bool deleteFile) void GridGameListView::remove(FileData *game, bool deleteFile)
{ {
// Actually delete the file on the filesystem.
if (deleteFile) if (deleteFile)
Utils::FileSystem::removeFile(game->getPath()); // actually delete the file on the filesystem Utils::FileSystem::removeFile(game->getPath());
FileData* parent = game->getParent(); 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(); std::vector<FileData*> siblings = parent->getChildrenListToDisplay();
auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game); auto gameIter = std::find(siblings.cbegin(), siblings.cend(), game);
int gamePos = (int)std::distance(siblings.cbegin(), gameIter); int gamePos = (int)std::distance(siblings.cbegin(), gameIter);
if (gameIter != siblings.cend()) if (gameIter != siblings.cend()) {
{
if ((gamePos + 1) < (int)siblings.size()) if ((gamePos + 1) < (int)siblings.size())
{
setCursor(siblings.at(gamePos + 1)); setCursor(siblings.at(gamePos + 1));
} else if ((gamePos - 1) > 0) { else if ((gamePos - 1) > 0)
setCursor(siblings.at(gamePos - 1)); setCursor(siblings.at(gamePos - 1));
}
} }
} }
mGrid.remove(game); mGrid.remove(game);
if(mGrid.size() == 0)
{ if (mGrid.size() == 0)
addPlaceholder(); addPlaceholder();
}
delete game; // remove before repopulating (removes from parent) // Remove before repopulating (removes from parent).
onFileChanged(parent, FILE_REMOVED); // update the view, with game removed // Update the view, with game removed.
delete game;
onFileChanged(parent, FILE_REMOVED);
} }
std::vector<TextComponent*> GridGameListView::getMDLabels() std::vector<TextComponent*> GridGameListView::getMDLabels()
@ -476,17 +488,16 @@ std::vector<HelpPrompt> GridGameListView::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;
if(Settings::getInstance()->getBool("QuickSystemSelect")) if (Settings::getInstance()->getBool("QuickSystemSelect"))
prompts.push_back(HelpPrompt("lr", "system")); prompts.push_back(HelpPrompt("lr", "system"));
prompts.push_back(HelpPrompt("up/down/left/right", "choose")); prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
prompts.push_back(HelpPrompt("a", "launch")); prompts.push_back(HelpPrompt("a", "launch"));
prompts.push_back(HelpPrompt("b", "back")); prompts.push_back(HelpPrompt("b", "back"));
if(!UIModeController::getInstance()->isUIModeKid()) if (!UIModeController::getInstance()->isUIModeKid())
prompts.push_back(HelpPrompt("select", "options")); prompts.push_back(HelpPrompt("select", "options"));
if(mRoot->getSystem()->isGameSystem()) if (mRoot->getSystem()->isGameSystem())
prompts.push_back(HelpPrompt("x", "random")); 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(); std::string prompt = CollectionSystemManager::get()->getEditingCollection();
prompts.push_back(HelpPrompt("y", prompt)); prompts.push_back(HelpPrompt("y", prompt));
} }

View file

@ -1,3 +1,9 @@
//
// GridGameListView.h
//
// Interface that defines a GameListView of the type 'grid'.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_GRID_GAME_LIST_VIEW_H
#define 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 initMDLabels();
void initMDValues(); 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; ImageComponent mMarquee;
VideoComponent* mVideo; VideoComponent* mVideo;

View file

@ -1,3 +1,9 @@
//
// IGameListView.cpp
//
// Interface that defines the minimum for a GameListView.
//
#include "views/gamelist/IGameListView.h" #include "views/gamelist/IGameListView.h"
#include "guis/GuiGamelistOptions.h" #include "guis/GuiGamelistOptions.h"
@ -8,16 +14,17 @@
bool IGameListView::input(InputConfig* config, Input input) bool IGameListView::input(InputConfig* config, Input input)
{ {
// select to open GuiGamelistOptions // Select button opens GuiGamelistOptions.
if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && input.value) if (!UIModeController::getInstance()->isUIModeKid() &&
{ config->isMappedTo("select", input) && input.value) {
mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem())); mWindow->pushGui(new GuiGamelistOptions(mWindow, this->mRoot->getSystem()));
return true; return true;
}
// Ctrl-R to reload a view when debugging // Ctrl-R reloads the view when debugging.
}else if(Settings::getInstance()->getBool("Debug") && config->getDeviceId() == DEVICE_KEYBOARD && else if (Settings::getInstance()->getBool("Debug") &&
(SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) && input.id == SDLK_r && input.value != 0) config->getDeviceId() == DEVICE_KEYBOARD &&
{ (SDL_GetModState() & (KMOD_LCTRL | KMOD_RCTRL)) &&
input.id == SDLK_r && input.value != 0) {
LOG(LogDebug) << "reloading view"; LOG(LogDebug) << "reloading view";
ViewController::get()->reloadGameListView(this, true); ViewController::get()->reloadGameListView(this, true);
return true; return true;
@ -46,8 +53,10 @@ void IGameListView::render(const Transform4x4f& parentTrans)
float scaleX = trans.r0().x(); float scaleX = trans.r0().x();
float scaleY = trans.r1().y(); float scaleY = trans.r1().y();
Vector2i pos((int)Math::round(trans.translation()[0]), (int)Math::round(trans.translation()[1])); Vector2i pos((int)Math::round(trans.translation()[0]),
Vector2i size((int)Math::round(mSize.x() * scaleX), (int)Math::round(mSize.y() * scaleY)); (int)Math::round(trans.translation()[1]));
Vector2i size((int)Math::round(mSize.x() * scaleX),
(int)Math::round(mSize.y() * scaleY));
Renderer::pushClipRect(pos, size); Renderer::pushClipRect(pos, size);
renderChildren(trans); renderChildren(trans);

View file

@ -1,3 +1,9 @@
//
// IGameListView.h
//
// Interface that defines the minimum for a GameListView.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_IGAME_LIST_VIEW_H
#define 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 class IGameListView : public GuiComponent
{ {
public: public:
IGameListView(Window* window, FileData* root) : GuiComponent(window), mRoot(root) IGameListView(
{ setSize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight()); } Window* window,
FileData* root)
: GuiComponent(window),
mRoot(root)
{ setSize((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight()); }
virtual ~IGameListView() {} 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. // 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. // Since sorts are recursive, that FileData's children probably changed too.
virtual void onFileChanged(FileData* file, FileChangeType change) = 0; virtual void onFileChanged(FileData* file, FileChangeType change) = 0;

View file

@ -1,3 +1,9 @@
//
// ISimpleGameListView.cpp
//
// Interface that defines a simple GameListView.
//
#include "views/gamelist/ISimpleGameListView.h" #include "views/gamelist/ISimpleGameListView.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
@ -7,8 +13,13 @@
#include "Sound.h" #include "Sound.h"
#include "SystemData.h" #include "SystemData.h"
ISimpleGameListView::ISimpleGameListView(Window* window, FileData* root) : IGameListView(window, root), ISimpleGameListView::ISimpleGameListView(
mHeaderText(window), mHeaderImage(window), mBackground(window) Window* window,
FileData* root)
: IGameListView(window, root),
mHeaderText(window),
mHeaderImage(window),
mBackground(window)
{ {
mHeaderText.setText("Logo Text"); mHeaderText.setText("Logo Text");
mHeaderText.setSize(mSize.x(), 0); mHeaderText.setSize(mSize.x(), 0);
@ -35,7 +46,7 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
mHeaderImage.applyTheme(theme, getName(), "logo", ALL); mHeaderImage.applyTheme(theme, getName(), "logo", ALL);
mHeaderText.applyTheme(theme, getName(), "logoText", ALL); mHeaderText.applyTheme(theme, getName(), "logoText", ALL);
// Remove old theme extras // Remove old theme extras.
for (auto extra : mThemeExtras) for (auto extra : mThemeExtras)
{ {
removeChild(extra); removeChild(extra);
@ -43,18 +54,16 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
} }
mThemeExtras.clear(); mThemeExtras.clear();
// Add new theme extras // Add new theme extras.
mThemeExtras = ThemeData::makeExtras(theme, getName(), mWindow); mThemeExtras = ThemeData::makeExtras(theme, getName(), mWindow);
for (auto extra : mThemeExtras) for (auto extra : mThemeExtras)
{
addChild(extra); addChild(extra);
}
if(mHeaderImage.hasImage()) if (mHeaderImage.hasImage()) {
{
removeChild(&mHeaderText); removeChild(&mHeaderText);
addChild(&mHeaderImage); addChild(&mHeaderImage);
}else{ }
else {
addChild(&mHeaderText); addChild(&mHeaderText);
removeChild(&mHeaderImage); removeChild(&mHeaderImage);
} }
@ -62,8 +71,8 @@ void ISimpleGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme
void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*change*/) void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*change*/)
{ {
// we could be tricky here to be efficient; // We could be tricky here to be efficient;
// but this shouldn't happen very often so we'll just always repopulate // but this shouldn't happen very often so we'll just always repopulate.
FileData* cursor = getCursor(); FileData* cursor = getCursor();
if (!cursor->isPlaceHolder()) { if (!cursor->isPlaceHolder()) {
populateList(cursor->getParent()->getChildrenListToDisplay()); populateList(cursor->getParent()->getChildrenListToDisplay());
@ -78,20 +87,17 @@ void ISimpleGameListView::onFileChanged(FileData* /*file*/, FileChangeType /*cha
bool ISimpleGameListView::input(InputConfig* config, Input input) bool ISimpleGameListView::input(InputConfig* config, Input input)
{ {
std::shared_ptr<Sound> soundfile; std::shared_ptr<Sound> soundfile;
if(input.value != 0) if (input.value != 0) {
{ if (config->isMappedTo("a", input)) {
if(config->isMappedTo("a", input))
{
FileData* cursor = getCursor(); FileData* cursor = getCursor();
if(cursor->getType() == GAME) if (cursor->getType() == GAME) {
{
launch(cursor); launch(cursor);
}else{ }
// it's a folder else {
if(cursor->getChildren().size() > 0) // It's a folder.
{ if (cursor->getChildren().size() > 0) {
mCursorStack.push(cursor); mCursorStack.push(cursor);
populateList(cursor->getChildrenListToDisplay()); populateList(cursor->getChildrenListToDisplay());
FileData* cursor = getCursor(); FileData* cursor = getCursor();
@ -100,14 +106,14 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
return true; return true;
}else if(config->isMappedTo("b", input)) }
{ else if (config->isMappedTo("b", input)) {
if(mCursorStack.size()) if (mCursorStack.size()) {
{
populateList(mCursorStack.top()->getParent()->getChildren()); populateList(mCursorStack.top()->getParent()->getChildren());
setCursor(mCursorStack.top()); setCursor(mCursorStack.top());
mCursorStack.pop(); mCursorStack.pop();
}else{ }
else {
navigationsounds.playThemeNavigationSound(BACKSOUND); navigationsounds.playThemeNavigationSound(BACKSOUND);
onFocusLost(); onFocusLost();
SystemData* systemToView = getCursor()->getSystem(); SystemData* systemToView = getCursor()->getSystem();
@ -119,57 +125,40 @@ bool ISimpleGameListView::input(InputConfig* config, Input input)
} }
return true; return true;
}else if(config->isMappedLike(getQuickSystemSelectRightButton(), input)) }
{ else if (config->isMappedLike(getQuickSystemSelectRightButton(), input)) {
if(Settings::getInstance()->getBool("QuickSystemSelect")) if (Settings::getInstance()->getBool("QuickSystemSelect")) {
{
onFocusLost(); onFocusLost();
ViewController::get()->goToNextGameList(); ViewController::get()->goToNextGameList();
return true; return true;
} }
}else if(config->isMappedLike(getQuickSystemSelectLeftButton(), input)) }
{ else if (config->isMappedLike(getQuickSystemSelectLeftButton(), input)) {
if(Settings::getInstance()->getBool("QuickSystemSelect")) if (Settings::getInstance()->getBool("QuickSystemSelect")) {
{
onFocusLost(); onFocusLost();
ViewController::get()->goToPrevGameList(); ViewController::get()->goToPrevGameList();
return true; return true;
} }
}else if (config->isMappedTo("x", input)) }
{ else if (config->isMappedTo("x", input)) {
if (mRoot->getSystem()->isGameSystem()) if (mRoot->getSystem()->isGameSystem()) {
{ // Go to random system game.
// go to random system game
navigationsounds.playThemeNavigationSound(SCROLLSOUND); navigationsounds.playThemeNavigationSound(SCROLLSOUND);
FileData* randomGame = getCursor()->getSystem()->getRandomGame(); FileData* randomGame = getCursor()->getSystem()->getRandomGame();
if (randomGame) if (randomGame)
{
setCursor(randomGame); setCursor(randomGame);
}
return true; return true;
} }
}else if (config->isMappedTo("y", input) && !UIModeController::getInstance()->isUIModeKid()) }
{ else if (config->isMappedTo("y", input) &&
if(mRoot->getSystem()->isGameSystem()) !UIModeController::getInstance()->isUIModeKid()) {
{ if (mRoot->getSystem()->isGameSystem()) {
navigationsounds.playThemeNavigationSound(FAVORITESOUND); navigationsounds.playThemeNavigationSound(FAVORITESOUND);
if(CollectionSystemManager::get()->toggleGameInCollection(getCursor())) if (CollectionSystemManager::get()->toggleGameInCollection(getCursor()))
{
return true; return true;
}
} }
} }
} }
return IGameListView::input(config, input); return IGameListView::input(config, input);
} }

View file

@ -1,3 +1,9 @@
//
// ISimpleGameListView.h
//
// Interface that defines a simple GameListView.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_ISIMPLE_GAME_LIST_VIEW_H
#define 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: public:
ISimpleGameListView(Window* window, FileData* root); ISimpleGameListView(Window* window, FileData* root);
virtual ~ISimpleGameListView() {} 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. // 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. // Since sorts are recursive, that FileData's children probably changed too.
virtual void onFileChanged(FileData* file, FileChangeType change); virtual void onFileChanged(FileData* file, FileChangeType change);

View file

@ -1,3 +1,9 @@
//
// VideoGameListView.cpp
//
// Interface that defines a GameListView of the type 'video'.
//
#include "views/gamelist/VideoGameListView.h" #include "views/gamelist/VideoGameListView.h"
#include "animations/LambdaAnimation.h" #include "animations/LambdaAnimation.h"
@ -11,25 +17,41 @@
#include "Settings.h" #include "Settings.h"
#endif #endif
VideoGameListView::VideoGameListView(Window* window, FileData* root) : VideoGameListView::VideoGameListView(
BasicGameListView(window, root), Window* window,
mDescContainer(window), mDescription(window), FileData* root)
mThumbnail(window), : BasicGameListView(window, root),
mMarquee(window), mDescContainer(window),
mImage(window), mDescription(window),
mVideo(nullptr),
mVideoPlaying(false),
mLblRating(window), mLblReleaseDate(window), mLblDeveloper(window), mLblPublisher(window), mThumbnail(window),
mLblGenre(window), mLblPlayers(window), mLblLastPlayed(window), mLblPlayCount(window), mMarquee(window),
mImage(window),
mVideo(nullptr),
mVideoPlaying(false),
mRating(window), mReleaseDate(window), mDeveloper(window), mPublisher(window), mLblRating(window),
mGenre(window), mPlayers(window), mLastPlayed(window), mPlayCount(window), mLblReleaseDate(window),
mName(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; const float padding = 0.01f;
// Create the correct type of video window // Create the correct type of video window.
#ifdef _RPI_ #ifdef _RPI_
if (Settings::getInstance()->getBool("VideoOmxPlayer")) if (Settings::getInstance()->getBool("VideoOmxPlayer"))
mVideo = new VideoPlayerComponent(window, ""); mVideo = new VideoPlayerComponent(window, "");
@ -44,7 +66,7 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT); mList.setAlignment(TextListComponent<FileData*>::ALIGN_LEFT);
mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); }); mList.setCursorChangedCallback([&](const CursorState& /*state*/) { updateInfoPanel(); });
// Thumbnail // Thumbnail.
mThumbnail.setOrigin(0.5f, 0.5f); mThumbnail.setOrigin(0.5f, 0.5f);
mThumbnail.setPosition(2.0f, 2.0f); mThumbnail.setPosition(2.0f, 2.0f);
mThumbnail.setVisible(false); mThumbnail.setVisible(false);
@ -52,30 +74,30 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
mThumbnail.setDefaultZIndex(35); mThumbnail.setDefaultZIndex(35);
addChild(&mThumbnail); addChild(&mThumbnail);
// Marquee // Marquee.
mMarquee.setOrigin(0.5f, 0.5f); mMarquee.setOrigin(0.5f, 0.5f);
mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f); mMarquee.setPosition(mSize.x() * 0.25f, mSize.y() * 0.10f);
mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f); mMarquee.setMaxSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.18f);
mMarquee.setDefaultZIndex(35); mMarquee.setDefaultZIndex(35);
addChild(&mMarquee); addChild(&mMarquee);
// Image // Image.
mImage.setOrigin(0.5f, 0.5f); 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.setPosition(mSize.x() * 0.25f, mList.getPosition().y() + mSize.y() * 0.2125f);
mImage.setVisible(false); mImage.setVisible(false);
mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f); mImage.setMaxSize(mSize.x() * (0.50f - 2*padding), mSize.y() * 0.4f);
mImage.setDefaultZIndex(30); mImage.setDefaultZIndex(30);
addChild(&mImage); addChild(&mImage);
// video // Video.
mVideo->setOrigin(0.5f, 0.5f); mVideo->setOrigin(0.5f, 0.5f);
mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f); mVideo->setPosition(mSize.x() * 0.25f, mSize.y() * 0.4f);
mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f); mVideo->setSize(mSize.x() * (0.5f - 2*padding), mSize.y() * 0.4f);
mVideo->setDefaultZIndex(30); mVideo->setDefaultZIndex(30);
addChild(mVideo); addChild(mVideo);
// metadata labels + values // Metadata labels + values.
mLblRating.setText("Rating: "); mLblRating.setText("Rating: ");
addChild(&mLblRating); addChild(&mLblRating);
addChild(&mRating); addChild(&mRating);
@ -110,7 +132,8 @@ VideoGameListView::VideoGameListView(Window* window, FileData* root) :
addChild(&mName); addChild(&mName);
mDescContainer.setPosition(mSize.x() * padding, mSize.y() * 0.65f); 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.setAutoScroll(true);
mDescContainer.setDefaultZIndex(40); mDescContainer.setDefaultZIndex(40);
addChild(&mDescContainer); addChild(&mDescContainer);
@ -133,10 +156,14 @@ void VideoGameListView::onThemeChanged(const std::shared_ptr<ThemeData>& theme)
BasicGameListView::onThemeChanged(theme); BasicGameListView::onThemeChanged(theme);
using namespace ThemeFlags; using namespace ThemeFlags;
mThumbnail.applyTheme(theme, getName(), "md_thumbnail", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mThumbnail.applyTheme(theme, getName(), "md_thumbnail",
mMarquee.applyTheme(theme, getName(), "md_marquee", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE);
mImage.applyTheme(theme, getName(), "md_image", POSITION | ThemeFlags::SIZE | Z_INDEX | ROTATION | VISIBLE); mMarquee.applyTheme(theme, getName(), "md_marquee",
mVideo->applyTheme(theme, getName(), "md_video", POSITION | ThemeFlags::SIZE | ThemeFlags::DELAY | Z_INDEX | ROTATION | VISIBLE); 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); mName.applyTheme(theme, getName(), "md_name", ALL);
initMDLabels(); 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" "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); labels[i]->applyTheme(theme, getName(), lblElements[i], ALL);
}
initMDValues(); initMDValues();
std::vector<GuiComponent*> values = getMDValues(); 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" "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); 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.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(); sortChildren();
} }
@ -185,15 +209,14 @@ void VideoGameListView::initMDLabels()
const float colSize = (mSize.x() * 0.48f) / colCount; const float colSize = (mSize.x() * 0.48f) / colCount;
const float rowPadding = 0.01f * mSize.y(); 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; const unsigned int row = i % rowCount;
Vector3f pos(0.0f, 0.0f, 0.0f); Vector3f pos(0.0f, 0.0f, 0.0f);
if(row == 0) if (row == 0) {
{
pos = start + Vector3f(colSize * (i / rowCount), 0, 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]; GuiComponent* lc = components[i-1];
pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0); pos = lc->getPosition() + Vector3f(0, lc->getSize().y() + rowPadding, 0);
} }
@ -222,24 +245,23 @@ void VideoGameListView::initMDValues()
float bottom = 0.0f; float bottom = 0.0f;
const float colSize = (mSize.x() * 0.48f) / 2; 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; 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]->setSize(colSize - labels[i]->getSize().x(), values[i]->getSize().y());
values[i]->setDefaultZIndex(40); values[i]->setDefaultZIndex(40);
float testBot = values[i]->getPosition().y() + values[i]->getSize().y(); float testBot = values[i]->getPosition().y() + values[i]->getSize().y();
if(testBot > bottom) if (testBot > bottom)
bottom = testBot; bottom = testBot;
} }
mDescContainer.setPosition(mDescContainer.getPosition().x(), bottom + mSize.y() * 0.01f); 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() void VideoGameListView::updateInfoPanel()
{ {
FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected(); FileData* file = (mList.size() == 0 || mList.isScrolling()) ? NULL : mList.getSelected();
@ -247,8 +269,7 @@ void VideoGameListView::updateInfoPanel()
Utils::FileSystem::removeFile(getTitlePath()); Utils::FileSystem::removeFile(getTitlePath());
bool fadingOut; bool fadingOut;
if(file == NULL) if (file == nullptr) {
{
mVideo->setVideo(""); mVideo->setVideo("");
mVideo->setImage(""); mVideo->setImage("");
mVideoPlaying = false; mVideoPlaying = false;
@ -256,14 +277,13 @@ void VideoGameListView::updateInfoPanel()
//mDescription.setText(""); //mDescription.setText("");
fadingOut = true; fadingOut = true;
}else{ }
else {
if (!mVideo->setVideo(file->getVideoPath())) if (!mVideo->setVideo(file->getVideoPath()))
{
mVideo->setDefaultVideo(); mVideo->setDefaultVideo();
}
mVideoPlaying = true; mVideoPlaying = true;
// mVideo->setImage(file->getThumbnailPath());
mVideo->setImage(file->getImagePath()); mVideo->setImage(file->getImagePath());
mThumbnail.setImage(file->getThumbnailPath()); mThumbnail.setImage(file->getThumbnailPath());
mMarquee.setImage(file->getMarqueePath()); mMarquee.setImage(file->getMarqueePath());
@ -280,8 +300,7 @@ void VideoGameListView::updateInfoPanel()
mPlayers.setValue(file->metadata.get("players")); mPlayers.setValue(file->metadata.get("players"));
mName.setValue(file->metadata.get("name")); mName.setValue(file->metadata.get("name"));
if(file->getType() == GAME) if (file->getType() == GAME) {
{
mLastPlayed.setValue(file->metadata.get("lastplayed")); mLastPlayed.setValue(file->metadata.get("lastplayed"));
mPlayCount.setValue(file->metadata.get("playcount")); mPlayCount.setValue(file->metadata.get("playcount"));
} }
@ -299,18 +318,13 @@ void VideoGameListView::updateInfoPanel()
std::vector<TextComponent*> labels = getMDLabels(); std::vector<TextComponent*> labels = getMDLabels();
comps.insert(comps.cend(), labels.cbegin(), labels.cend()); 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; GuiComponent* comp = *it;
// an animation is playing // An animation is playing, then animate if reverse != fadingOut
// then animate if reverse != fadingOut // An animation is not playing, then animate if opacity != our target opacity
// an animation is not playing if ((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) ||
// then animate if opacity != our target opacity (!comp->isAnimationPlaying(0) && comp->getOpacity() != (fadingOut ? 0 : 255))) {
if((comp->isAnimationPlaying(0) && comp->isAnimationReversed(0) != fadingOut) || auto func = [comp](float t) {
(!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->setOpacity((unsigned char)(Math::lerp(0.0f, 1.0f, t)*255));
}; };
comp->setAnimation(new LambdaAnimation(func, 150), 0, nullptr, fadingOut); 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); Vector3f target(screenWidth / 2.0f, screenHeight / 2.0f, 0);
if(mMarquee.hasImage() && if (mMarquee.hasImage() &&
(mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f && (mMarquee.getPosition().x() < screenWidth && mMarquee.getPosition().x() > 0.0f &&
mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f)) mMarquee.getPosition().y() < screenHeight && mMarquee.getPosition().y() > 0.0f))
{
target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0); target = Vector3f(mMarquee.getCenter().x(), mMarquee.getCenter().y(), 0);
}
else if(mThumbnail.hasImage() && else if (mThumbnail.hasImage() &&
(mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f && (mThumbnail.getPosition().x() < screenWidth && mThumbnail.getPosition().x() > 2.0f &&
mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f)) mThumbnail.getPosition().y() < screenHeight && mThumbnail.getPosition().y() > 2.0f))
{
target = Vector3f(mThumbnail.getCenter().x(), mThumbnail.getCenter().y(), 0); target = Vector3f(mThumbnail.getCenter().x(), mThumbnail.getCenter().y(), 0);
}
else if(mImage.hasImage() && else if (mImage.hasImage() &&
(mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f && (mImage.getPosition().x() < screenWidth && mImage.getPosition().x() > 2.0f &&
mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f)) mImage.getPosition().y() < screenHeight && mImage.getPosition().y() > 2.0f))
{
target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0); target = Vector3f(mImage.getCenter().x(), mImage.getCenter().y(), 0);
}
else if(mHeaderImage.hasImage() && else if (mHeaderImage.hasImage() &&
(mHeaderImage.getPosition().x() < screenWidth && mHeaderImage.getPosition().x() > 0.0f && (mHeaderImage.getPosition().x() < screenWidth &&
mHeaderImage.getPosition().y() < screenHeight && mHeaderImage.getPosition().y() > 0.0f)) mHeaderImage.getPosition().x() > 0.0f &&
{ mHeaderImage.getPosition().y() < screenHeight &&
mHeaderImage.getPosition().y() > 0.0f))
target = Vector3f(mHeaderImage.getCenter().x(), mHeaderImage.getCenter().y(), 0); target = Vector3f(mHeaderImage.getCenter().x(), mHeaderImage.getCenter().y(), 0);
}
else if(mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f && else if (mVideo->getPosition().x() < screenWidth && mVideo->getPosition().x() > 0.0f &&
mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f) mVideo->getPosition().y() < screenHeight && mVideo->getPosition().y() > 0.0f)
{
target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0); target = Vector3f(mVideo->getCenter().x(), mVideo->getCenter().y(), 0);
}
ViewController::get()->launch(game, target); ViewController::get()->launch(game, target);
} }

View file

@ -1,3 +1,9 @@
//
// VideoGameListView.h
//
// Interface that defines a GameListView of the type 'video'.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H #ifndef ES_APP_VIEWS_GAME_LIST_VIDEO_GAME_LIST_VIEW_H
#define 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; VideoComponent* mVideo;
ImageComponent mImage; 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; RatingComponent mRating;
DateTimeComponent mReleaseDate; DateTimeComponent mReleaseDate;

View file

@ -1,3 +1,9 @@
//
// GuiComponent.cpp
//
// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation.
//
#include "GuiComponent.h" #include "GuiComponent.h"
#include "animations/Animation.h" #include "animations/Animation.h"
@ -8,11 +14,19 @@
#include "Window.h" #include "Window.h"
#include <algorithm> #include <algorithm>
GuiComponent::GuiComponent(Window* window) : mWindow(window), mParent(NULL), mOpacity(255), GuiComponent::GuiComponent(Window* window)
mPosition(Vector3f::Zero()), mOrigin(Vector2f::Zero()), mRotationOrigin(0.5, 0.5), : mWindow(window),
mSize(Vector2f::Zero()), mTransform(Transform4x4f::Identity()), mIsProcessing(false), mVisible(true) 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; mAnimationMap[i] = NULL;
} }
@ -22,18 +36,17 @@ GuiComponent::~GuiComponent()
cancelAllAnimations(); cancelAllAnimations();
if(mParent) if (mParent)
mParent->removeChild(this); mParent->removeChild(this);
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->setParent(NULL); getChild(i)->setParent(NULL);
} }
bool GuiComponent::input(InputConfig* config, Input input) bool GuiComponent::input(InputConfig* config, Input input)
{ {
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++) {
{ if (getChild(i)->input(config, input))
if(getChild(i)->input(config, input))
return true; return true;
} }
@ -42,16 +55,14 @@ bool GuiComponent::input(InputConfig* config, Input input)
void GuiComponent::updateSelf(int deltaTime) 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); advanceAnimation(i, deltaTime);
} }
void GuiComponent::updateChildren(int 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); getChild(i)->update(deltaTime);
}
} }
void GuiComponent::update(int deltaTime) void GuiComponent::update(int deltaTime)
@ -71,10 +82,8 @@ void GuiComponent::render(const Transform4x4f& parentTrans)
void GuiComponent::renderChildren(const Transform4x4f& transform) const 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); getChild(i)->render(transform);
}
} }
Vector3f GuiComponent::getPosition() const Vector3f GuiComponent::getPosition() const
@ -175,12 +184,12 @@ Vector2f GuiComponent::getCenter() const
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2); mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
} }
//Children stuff. // Children stuff.
void GuiComponent::addChild(GuiComponent* cmp) void GuiComponent::addChild(GuiComponent* cmp)
{ {
mChildren.push_back(cmp); mChildren.push_back(cmp);
if(cmp->getParent()) if (cmp->getParent())
cmp->getParent()->removeChild(cmp); cmp->getParent()->removeChild(cmp);
cmp->setParent(this); cmp->setParent(this);
@ -188,20 +197,16 @@ void GuiComponent::addChild(GuiComponent* cmp)
void GuiComponent::removeChild(GuiComponent* cmp) void GuiComponent::removeChild(GuiComponent* cmp)
{ {
if(!cmp->getParent()) if (!cmp->getParent())
return; return;
if(cmp->getParent() != this) if (cmp->getParent() != this)
{
LOG(LogError) << "Tried to remove child from incorrect parent!"; LOG(LogError) << "Tried to remove child from incorrect parent!";
}
cmp->setParent(NULL); cmp->setParent(NULL);
for(auto i = mChildren.cbegin(); i != mChildren.cend(); i++) for (auto i = mChildren.cbegin(); i != mChildren.cend(); i++) {
{ if (*i == cmp) {
if(*i == cmp)
{
mChildren.erase(i); mChildren.erase(i);
return; return;
} }
@ -215,7 +220,7 @@ void GuiComponent::clearChildren()
void GuiComponent::sortChildren() 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(); return b->getZIndex() > a->getZIndex();
}); });
} }
@ -248,10 +253,8 @@ unsigned char GuiComponent::getOpacity() const
void GuiComponent::setOpacity(unsigned char opacity) void GuiComponent::setOpacity(unsigned char opacity)
{ {
mOpacity = opacity; mOpacity = opacity;
for(auto it = mChildren.cbegin(); it != mChildren.cend(); it++) for (auto it = mChildren.cbegin(); it != mChildren.cend(); it++)
{
(*it)->setOpacity(opacity); (*it)->setOpacity(opacity);
}
} }
const Transform4x4f& GuiComponent::getTransform() const Transform4x4f& GuiComponent::getTransform()
@ -259,28 +262,26 @@ const Transform4x4f& GuiComponent::getTransform()
mTransform = Transform4x4f::Identity(); mTransform = Transform4x4f::Identity();
mTransform.translate(mPosition); mTransform.translate(mPosition);
if (mScale != 1.0) if (mScale != 1.0)
{
mTransform.scale(mScale); mTransform.scale(mScale);
} if (mRotation != 0.0) {
if (mRotation != 0.0)
{
// Calculate offset as difference between origin and rotation origin // Calculate offset as difference between origin and rotation origin
Vector2f rotationSize = getRotationSize(); Vector2f rotationSize = getRotationSize();
float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x(); float xOff = (mOrigin.x() - mRotationOrigin.x()) * rotationSize.x();
float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y(); float yOff = (mOrigin.y() - mRotationOrigin.y()) * rotationSize.y();
// transform to offset point // Transform to offset point
if (xOff != 0.0 || yOff != 0.0) if (xOff != 0.0 || yOff != 0.0)
mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f)); mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f));
// apply rotation transform // Apply rotation transform
mTransform.rotateZ(mRotation); mTransform.rotateZ(mRotation);
// Tranform back to original point // Tranform back to original point
if (xOff != 0.0 || yOff != 0.0) if (xOff != 0.0 || yOff != 0.0)
mTransform.translate(Vector3f(xOff, yOff, 0.0f)); 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; return mTransform;
} }
@ -295,32 +296,31 @@ std::string GuiComponent::getValue() const
void GuiComponent::textInput(const char* text) 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); (*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); assert(slot < MAX_ANIMATIONS);
AnimationController* oldAnim = mAnimationMap[slot]; AnimationController* oldAnim = mAnimationMap[slot];
mAnimationMap[slot] = new AnimationController(anim, delay, finishedCallback, reverse); mAnimationMap[slot] = new AnimationController(anim, delay, finishedCallback, reverse);
if(oldAnim) if (oldAnim)
delete oldAnim; delete oldAnim;
} }
bool GuiComponent::stopAnimation(unsigned char slot) bool GuiComponent::stopAnimation(unsigned char slot)
{ {
assert(slot < MAX_ANIMATIONS); assert(slot < MAX_ANIMATIONS);
if(mAnimationMap[slot]) if (mAnimationMap[slot]) {
{
delete mAnimationMap[slot]; delete mAnimationMap[slot];
mAnimationMap[slot] = NULL; mAnimationMap[slot] = NULL;
return true; return true;
}else{ }
else {
return false; return false;
} }
} }
@ -328,13 +328,13 @@ bool GuiComponent::stopAnimation(unsigned char slot)
bool GuiComponent::cancelAnimation(unsigned char slot) bool GuiComponent::cancelAnimation(unsigned char slot)
{ {
assert(slot < MAX_ANIMATIONS); assert(slot < MAX_ANIMATIONS);
if(mAnimationMap[slot]) if (mAnimationMap[slot]) {
{
mAnimationMap[slot]->removeFinishedCallback(); mAnimationMap[slot]->removeFinishedCallback();
delete mAnimationMap[slot]; delete mAnimationMap[slot];
mAnimationMap[slot] = NULL; mAnimationMap[slot] = NULL;
return true; return true;
}else{ }
else {
return false; return false;
} }
} }
@ -342,16 +342,17 @@ bool GuiComponent::cancelAnimation(unsigned char slot)
bool GuiComponent::finishAnimation(unsigned char slot) bool GuiComponent::finishAnimation(unsigned char slot)
{ {
assert(slot < MAX_ANIMATIONS); assert(slot < MAX_ANIMATIONS);
if(mAnimationMap[slot]) if (mAnimationMap[slot]) {
{ // Skip to animation's end
// skip to animation's end const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]->
const bool done = mAnimationMap[slot]->update(mAnimationMap[slot]->getAnimation()->getDuration() - mAnimationMap[slot]->getTime()); getAnimation()->getDuration() - mAnimationMap[slot]->getTime());
assert(done); assert(done);
delete mAnimationMap[slot]; // will also call finishedCallback delete mAnimationMap[slot]; // Will also call finishedCallback
mAnimationMap[slot] = NULL; mAnimationMap[slot] = NULL;
return true; return true;
}else{ }
else {
return false; return false;
} }
} }
@ -360,29 +361,28 @@ bool GuiComponent::advanceAnimation(unsigned char slot, unsigned int time)
{ {
assert(slot < MAX_ANIMATIONS); assert(slot < MAX_ANIMATIONS);
AnimationController* anim = mAnimationMap[slot]; AnimationController* anim = mAnimationMap[slot];
if(anim) if (anim) {
{
bool done = anim->update(time); bool done = anim->update(time);
if(done) if (done) {
{
mAnimationMap[slot] = NULL; mAnimationMap[slot] = NULL;
delete anim; delete anim;
} }
return true; return true;
}else{ }
else {
return false; return false;
} }
} }
void GuiComponent::stopAllAnimations() void GuiComponent::stopAllAnimations()
{ {
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
stopAnimation(i); stopAnimation(i);
} }
void GuiComponent::cancelAllAnimations() void GuiComponent::cancelAllAnimations()
{ {
for(unsigned char i = 0; i < MAX_ANIMATIONS; i++) for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
cancelAnimation(i); cancelAnimation(i);
} }
@ -403,41 +403,44 @@ int GuiComponent::getAnimationTime(unsigned char slot) const
return mAnimationMap[slot]->getTime(); 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, ""); const ThemeData::ThemeElement* elem = theme->getElement(view, element, "");
if(!elem) if (!elem)
return; return;
using namespace ThemeFlags; using namespace ThemeFlags;
if(properties & POSITION && elem->has("pos")) if (properties & POSITION && elem->has("pos")) {
{
Vector2f denormalized = elem->get<Vector2f>("pos") * scale; Vector2f denormalized = elem->get<Vector2f>("pos") * scale;
setPosition(Vector3f(denormalized.x(), denormalized.y(), 0)); 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); setSize(elem->get<Vector2f>("size") * scale);
// position + size also implies origin // Position + size also implies origin
if((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) && elem->has("origin")) if ((properties & ORIGIN || (properties & POSITION &&
properties & ThemeFlags::SIZE)) && elem->has("origin")) {
setOrigin(elem->get<Vector2f>("origin")); setOrigin(elem->get<Vector2f>("origin"));
}
if(properties & ThemeFlags::ROTATION) { if (properties & ThemeFlags::ROTATION) {
if(elem->has("rotation")) if (elem->has("rotation"))
setRotationDegrees(elem->get<float>("rotation")); setRotationDegrees(elem->get<float>("rotation"));
if(elem->has("rotationOrigin")) if (elem->has("rotationOrigin"))
setRotationOrigin(elem->get<Vector2f>("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")); setZIndex(elem->get<float>("zIndex"));
else else
setZIndex(getDefaultZIndex()); setZIndex(getDefaultZIndex());
if(properties & ThemeFlags::VISIBLE && elem->has("visible")) if (properties & ThemeFlags::VISIBLE && elem->has("visible"))
setVisible(elem->get<bool>("visible")); setVisible(elem->get<bool>("visible"));
else else
setVisible(true); setVisible(true);
@ -445,15 +448,14 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std
void GuiComponent::updateHelpPrompts() void GuiComponent::updateHelpPrompts()
{ {
if(getParent()) if (getParent()) {
{
getParent()->updateHelpPrompts(); getParent()->updateHelpPrompts();
return; return;
} }
std::vector<HelpPrompt> prompts = getHelpPrompts(); std::vector<HelpPrompt> prompts = getHelpPrompts();
if(mWindow->peekGui() == this) if (mWindow->peekGui() == this)
mWindow->setHelpPrompts(prompts, getHelpStyle()); mWindow->setHelpPrompts(prompts, getHelpStyle());
} }
@ -469,30 +471,30 @@ bool GuiComponent::isProcessing() const
void GuiComponent::onShow() void GuiComponent::onShow()
{ {
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->onShow(); getChild(i)->onShow();
} }
void GuiComponent::onHide() void GuiComponent::onHide()
{ {
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->onHide(); getChild(i)->onHide();
} }
void GuiComponent::onScreenSaverActivate() void GuiComponent::onScreenSaverActivate()
{ {
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->onScreenSaverActivate(); getChild(i)->onScreenSaverActivate();
} }
void GuiComponent::onScreenSaverDeactivate() void GuiComponent::onScreenSaverDeactivate()
{ {
for(unsigned int i = 0; i < getChildCount(); i++) for (unsigned int i = 0; i < getChildCount(); i++)
getChild(i)->onScreenSaverDeactivate(); getChild(i)->onScreenSaverDeactivate();
} }
void GuiComponent::topWindow(bool isTop) 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); getChild(i)->topWindow(isTop);
} }

View file

@ -1,3 +1,9 @@
//
// GuiComponent.h
//
// Basic GUI component handling such as placement, rotation, Z-order, rendering and animation.
//
#pragma once #pragma once
#ifndef ES_CORE_GUI_COMPONENT_H #ifndef ES_CORE_GUI_COMPONENT_H
#define ES_CORE_GUI_COMPONENT_H #define ES_CORE_GUI_COMPONENT_H
@ -25,36 +31,47 @@ public:
virtual void textInput(const char* text); virtual void textInput(const char* text);
//Called when input is received. // Called when input is received.
//Return true if the input is consumed, false if it should continue to be passed to other children. // 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); 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); virtual void update(int deltaTime);
//Called when it's time to render. By default, just calls renderChildren(parentTrans * getTransform()). // Called when it's time to render.
//You probably want to override this like so: // By default, just calls renderChildren(parentTrans * getTransform()).
//1. Calculate the new transform that your control will draw at with Transform4x4f t = parentTrans * getTransform(). // You probably want to override this like so:
//2. Set the renderer to use that new transform as the model matrix - Renderer::setMatrix(t); // 1. Calculate the new transform that your control will draw at with
//3. Draw your component. // Transform4x4f t = parentTrans * getTransform().
//4. Tell your children to render, based on your component's transform - renderChildren(t). // 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); virtual void render(const Transform4x4f& parentTrans);
Vector3f getPosition() const; 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); void setPosition(float x, float y, float z = 0.0f);
virtual void onPositionChanged() {}; 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; Vector2f getOrigin() const;
void setOrigin(float originX, float originY); void setOrigin(float originX, float originY);
inline void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); } inline void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); }
virtual void onOriginChanged() {}; 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; Vector2f getRotationOrigin() const;
void setRotationOrigin(float originX, float originY); 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; virtual Vector2f getSize() const;
inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); } inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); }
@ -92,15 +109,22 @@ public:
unsigned int getChildCount() const; unsigned int getChildCount() const;
GuiComponent* getChild(unsigned int i) 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 isAnimationPlaying(unsigned char slot) const;
bool isAnimationReversed(unsigned char slot) const; bool isAnimationReversed(unsigned char slot) const;
int getAnimationTime(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 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). // Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving
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). // things in their current state. 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). 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 stopAllAnimations();
void cancelAllAnimations(); void cancelAllAnimations();
@ -124,7 +148,8 @@ public:
// Default implementation just handles <pos> and <size> tags as normalized float pairs. // 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. // 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. // Returns a list of help prompts.
virtual std::vector<HelpPrompt> getHelpPrompts() { return std::vector<HelpPrompt>(); }; 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) // Returns true if the component is busy doing background processing (e.g. HTTP downloads)
bool isProcessing() const; bool isProcessing() const;
const static unsigned char MAX_ANIMATIONS = 4;
protected: protected:
void renderChildren(const Transform4x4f& transform) const; void renderChildren(const Transform4x4f& transform) const;
void updateSelf(int deltaTime); // updates animations void updateSelf(int deltaTime); // Updates animations
void updateChildren(int deltaTime); // updates animations void updateChildren(int deltaTime); // Updates animations
unsigned char mOpacity; unsigned char mOpacity;
Window* mWindow; Window* mWindow;
@ -162,11 +189,9 @@ protected:
bool mIsProcessing; bool mIsProcessing;
bool mVisible; bool mVisible;
public:
const static unsigned char MAX_ANIMATIONS = 4;
private: private:
Transform4x4f mTransform; //Don't access this directly! Use getTransform()! // Don't access this directly! Use getTransform()!
Transform4x4f mTransform;
AnimationController* mAnimationMap[MAX_ANIMATIONS]; AnimationController* mAnimationMap[MAX_ANIMATIONS];
}; };

View file

@ -68,6 +68,7 @@ void Settings::setDefaults()
mStringMap["StartupSystem"] = ""; mStringMap["StartupSystem"] = "";
mBoolMap["DisableKidStartMenu"] = true; mBoolMap["DisableKidStartMenu"] = true;
mStringMap["MediaDirectory"] = ""; mStringMap["MediaDirectory"] = "";
mStringMap["DefaultSortOrder"] = "";
mBoolMap["VSync"] = true; mBoolMap["VSync"] = true;
@ -139,9 +140,9 @@ void Settings::setDefaults()
mStringMap["CollectionSystemsAuto"] = ""; mStringMap["CollectionSystemsAuto"] = "";
mStringMap["CollectionSystemsCustom"] = ""; mStringMap["CollectionSystemsCustom"] = "";
mBoolMap["CollectionShowSystemInfo"] = true; mBoolMap["CollectionShowSystemInfo"] = true;
mBoolMap["SortAllSystems"] = false;
mBoolMap["UseCustomCollectionsSystem"] = true; mBoolMap["UseCustomCollectionsSystem"] = true;
mBoolMap["FavFirstCustom"] = true;
mBoolMap["FavoritesFirst"] = true; mBoolMap["FavoritesFirst"] = true;
mBoolMap["LaunchstringOverride"] = true; mBoolMap["LaunchstringOverride"] = true;

View file

@ -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 "guis/GuiComplexTextEditPopup.h"
#include "components/ButtonComponent.h" #include "components/ButtonComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "components/TextEditComponent.h" #include "components/TextEditComponent.h"
GuiComplexTextEditPopup::GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2, GuiComplexTextEditPopup::GuiComplexTextEditPopup(
const std::string& initValue, const std::function<void(const std::string&)>& okCallback, bool multiLine, const char* acceptBtnText) Window* window,
: GuiComponent(window), mBackground(window, ":/frame.png"), mGrid(window, Vector2i(1, 5)), mMultiLine(multiLine) 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(&mBackground);
addChild(&mGrid); addChild(&mGrid);
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title), Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER); Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2, Font::get(FONT_SIZE_SMALL), 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 = std::make_shared<TextEditComponent>(mWindow);
mText->setValue(initValue); mText->setValue(initValue);
if(!multiLine) if (!multiLine)
mText->setCursor(initValue.size()); mText->setCursor(initValue.size());
std::vector< std::shared_ptr<ButtonComponent> > buttons; 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, acceptBtnText, acceptBtnText,
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "LOAD", "load default string", [this, infoString2] { mText->setValue(infoString2); })); [this, okCallback] { okCallback(mText->getValue()); delete this; }));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear string", [this] { mText->setValue(""); })); buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "LOAD", "load default string",
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes", [this] { delete this; })); [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); mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true); mGrid.setEntry(mTitle, Vector2i(0, 0), false, true);
mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true); mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true);
mGrid.setEntry(mInfoString2, Vector2i(0, 2), 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.setEntry(mButtonGrid, Vector2i(0, 4), true, false);
mGrid.setRowHeightPerc(1, 0.15, true); mGrid.setRowHeightPerc(1, 0.15, true);
float textHeight = mText->getFont()->getHeight(); float textHeight = mText->getFont()->getHeight();
if(multiLine)
if (multiLine)
textHeight *= 6; textHeight *= 6;
mText->setSize(0, textHeight); mText->setSize(0, textHeight);
setSize(Renderer::getScreenWidth() * 0.75f, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() + 220); setSize(Renderer::getScreenWidth() * 0.75f,mTitle->getFont()->getHeight() +
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() - mSize.y()) / 2); textHeight + mButtonGrid->getSize().y() + 220);
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
(Renderer::getScreenHeight() - mSize.y()) / 2);
} }
void GuiComplexTextEditPopup::onSizeChanged() void GuiComplexTextEditPopup::onSizeChanged()
@ -51,7 +80,7 @@ void GuiComplexTextEditPopup::onSizeChanged()
mText->setSize(mSize.x() - 40, mText->getSize().y()); mText->setSize(mSize.x() - 40, mText->getSize().y());
// update grid // Update grid
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y()); mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y() / mSize.y()); mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y() / mSize.y());
mGrid.setSize(mSize); mGrid.setSize(mSize);
@ -59,12 +88,11 @@ void GuiComplexTextEditPopup::onSizeChanged()
bool GuiComplexTextEditPopup::input(InputConfig* config, Input input) bool GuiComplexTextEditPopup::input(InputConfig* config, Input input)
{ {
if(GuiComponent::input(config, input)) if (GuiComponent::input(config, input))
return true; return true;
// pressing back when not text editing closes us // Pressing back button when not text editing closes us
if(config->isMappedTo("b", input) && input.value) if (config->isMappedTo("b", input) && input.value) {
{
delete this; delete this;
return true; return true;
} }

View file

@ -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 #pragma once
#ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H #ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H
#define 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 class GuiComplexTextEditPopup : public GuiComponent
{ {
public: public:
GuiComplexTextEditPopup(Window* window, const std::string& title, const std::string& infoString1, const std::string& infoString2, GuiComplexTextEditPopup(
const std::string& initValue, const std::function<void(const std::string&)>& okCallback, bool multiLine, const char* acceptBtnText = "OK"); 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); bool input(InputConfig* config, Input input);
void onSizeChanged(); void onSizeChanged();