mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2025-02-06 15:25:39 +00:00
Formatted the es-core source tree using clang-format.
This commit is contained in:
parent
af5e32e121
commit
23fdc00044
|
@ -12,7 +12,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
enum AsyncHandleStatus {
|
enum AsyncHandleStatus {
|
||||||
ASYNC_IN_PROGRESS,
|
ASYNC_IN_PROGRESS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
ASYNC_ERROR,
|
ASYNC_ERROR,
|
||||||
ASYNC_DONE
|
ASYNC_DONE
|
||||||
};
|
};
|
||||||
|
@ -21,17 +21,24 @@ enum AsyncHandleStatus {
|
||||||
class AsyncHandle
|
class AsyncHandle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AsyncHandle() : mStatus(ASYNC_IN_PROGRESS) {};
|
AsyncHandle()
|
||||||
virtual ~AsyncHandle() {};
|
: mStatus(ASYNC_IN_PROGRESS)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual ~AsyncHandle() {}
|
||||||
|
|
||||||
virtual void update() = 0;
|
virtual void update() = 0;
|
||||||
|
|
||||||
// Update and return the latest status.
|
// Update and return the latest status.
|
||||||
inline AsyncHandleStatus status() { update(); return mStatus; }
|
AsyncHandleStatus status()
|
||||||
|
{
|
||||||
|
update();
|
||||||
|
return mStatus;
|
||||||
|
}
|
||||||
|
|
||||||
// User-friendly string of our current status.
|
// User-friendly string of our current status.
|
||||||
// Will return error message if status() == SEARCH_ERROR.
|
// Will return error message if status() == SEARCH_ERROR.
|
||||||
inline std::string getStatusString()
|
std::string getStatusString()
|
||||||
{
|
{
|
||||||
switch (mStatus) {
|
switch (mStatus) {
|
||||||
case ASYNC_IN_PROGRESS:
|
case ASYNC_IN_PROGRESS:
|
||||||
|
@ -46,8 +53,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void setStatus(AsyncHandleStatus status) { mStatus = status; }
|
void setStatus(AsyncHandleStatus status) { mStatus = status; }
|
||||||
inline void setError(const std::string& error) { setStatus(ASYNC_ERROR); mError = error; }
|
void setError(const std::string& error)
|
||||||
|
{
|
||||||
|
setStatus(ASYNC_ERROR);
|
||||||
|
mError = error;
|
||||||
|
}
|
||||||
|
|
||||||
std::string mError;
|
std::string mError;
|
||||||
AsyncHandleStatus mStatus;
|
AsyncHandleStatus mStatus;
|
||||||
|
|
|
@ -19,17 +19,20 @@ std::vector<std::shared_ptr<Sound>> AudioManager::sSoundVector;
|
||||||
SDL_AudioDeviceID AudioManager::sAudioDevice = 0;
|
SDL_AudioDeviceID AudioManager::sAudioDevice = 0;
|
||||||
SDL_AudioSpec AudioManager::sAudioFormat;
|
SDL_AudioSpec AudioManager::sAudioFormat;
|
||||||
SDL_AudioStream* AudioManager::sConversionStream;
|
SDL_AudioStream* AudioManager::sConversionStream;
|
||||||
|
|
||||||
bool AudioManager::sMuteStream = false;
|
bool AudioManager::sMuteStream = false;
|
||||||
bool AudioManager::sHasAudioDevice = true;
|
bool AudioManager::sHasAudioDevice = true;
|
||||||
bool AudioManager::mIsClearingStream = false;
|
bool AudioManager::mIsClearingStream = false;
|
||||||
|
|
||||||
AudioManager::AudioManager()
|
AudioManager::AudioManager()
|
||||||
{
|
{
|
||||||
|
// Init on construction.
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioManager::~AudioManager()
|
AudioManager::~AudioManager()
|
||||||
{
|
{
|
||||||
|
// Deinit on destruction.
|
||||||
deinit();
|
deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +77,7 @@ void AudioManager::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
sAudioDevice = SDL_OpenAudioDevice(0, 0, &sRequestedAudioFormat, &sAudioFormat,
|
sAudioDevice = SDL_OpenAudioDevice(0, 0, &sRequestedAudioFormat, &sAudioFormat,
|
||||||
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
SDL_AUDIO_ALLOW_ANY_CHANGE);
|
||||||
|
|
||||||
if (sAudioDevice == 0) {
|
if (sAudioDevice == 0) {
|
||||||
LOG(LogError) << "Unable to open audio device: " << SDL_GetError();
|
LOG(LogError) << "Unable to open audio device: " << SDL_GetError();
|
||||||
|
@ -82,29 +85,30 @@ void AudioManager::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sAudioFormat.freq != sRequestedAudioFormat.freq) {
|
if (sAudioFormat.freq != sRequestedAudioFormat.freq) {
|
||||||
LOG(LogDebug) << "AudioManager::init(): Requested sample rate " <<
|
LOG(LogDebug) << "AudioManager::init(): Requested sample rate "
|
||||||
std::to_string(sRequestedAudioFormat.freq) << " could not be "
|
<< std::to_string(sRequestedAudioFormat.freq)
|
||||||
"set, obtained " << std::to_string(sAudioFormat.freq);
|
<< " could not be set, obtained " << std::to_string(sAudioFormat.freq);
|
||||||
}
|
}
|
||||||
if (sAudioFormat.format != sRequestedAudioFormat.format) {
|
if (sAudioFormat.format != sRequestedAudioFormat.format) {
|
||||||
LOG(LogDebug) << "AudioManager::init(): Requested format " <<
|
LOG(LogDebug) << "AudioManager::init(): Requested format "
|
||||||
std::to_string(sRequestedAudioFormat.format) << " could not be "
|
<< std::to_string(sRequestedAudioFormat.format)
|
||||||
"set, obtained " << std::to_string(sAudioFormat.format);
|
<< " could not be set, obtained " << std::to_string(sAudioFormat.format);
|
||||||
}
|
}
|
||||||
if (sAudioFormat.channels != sRequestedAudioFormat.channels) {
|
if (sAudioFormat.channels != sRequestedAudioFormat.channels) {
|
||||||
LOG(LogDebug) << "AudioManager::init(): Requested channel count " <<
|
LOG(LogDebug) << "AudioManager::init(): Requested channel count "
|
||||||
std::to_string(sRequestedAudioFormat.channels) << " could not be "
|
<< std::to_string(sRequestedAudioFormat.channels)
|
||||||
"set, obtained " << std::to_string(sAudioFormat.channels);
|
<< " could not be set, obtained " << std::to_string(sAudioFormat.channels);
|
||||||
}
|
}
|
||||||
#if defined(_WIN64) || defined(__APPLE__)
|
#if defined(_WIN64) || defined(__APPLE__)
|
||||||
// Beats me why the buffer size is not divided by the channel count on some operating systems.
|
// Beats me why the buffer size is not divided by the channel count on some operating systems.
|
||||||
if (sAudioFormat.samples != sRequestedAudioFormat.samples) {
|
if (sAudioFormat.samples != sRequestedAudioFormat.samples) {
|
||||||
#else
|
#else
|
||||||
if (sAudioFormat.samples != sRequestedAudioFormat.samples / sRequestedAudioFormat.channels) {
|
if (sAudioFormat.samples != sRequestedAudioFormat.samples / sRequestedAudioFormat.channels) {
|
||||||
#endif
|
#endif
|
||||||
LOG(LogDebug) << "AudioManager::init(): Requested sample buffer size " <<
|
LOG(LogDebug) << "AudioManager::init(): Requested sample buffer size "
|
||||||
std::to_string(sRequestedAudioFormat.samples / sRequestedAudioFormat.channels) <<
|
<< std::to_string(sRequestedAudioFormat.samples /
|
||||||
" could not be set, obtained " << std::to_string(sAudioFormat.samples);
|
sRequestedAudioFormat.channels)
|
||||||
|
<< " could not be set, obtained " << std::to_string(sAudioFormat.samples);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Just in case someone changed the es_settings.xml file manually to invalid values.
|
// Just in case someone changed the es_settings.xml file manually to invalid values.
|
||||||
|
@ -126,7 +130,7 @@ void AudioManager::deinit()
|
||||||
// user on some operating systems such as macOS, and it's annoying to have a crash at the
|
// user on some operating systems such as macOS, and it's annoying to have a crash at the
|
||||||
// end of debugging session. So we'll simply disable the function until it has been properly
|
// end of debugging session. So we'll simply disable the function until it has been properly
|
||||||
// fixed in the SDL library.
|
// fixed in the SDL library.
|
||||||
// SDL_FreeAudioStream(sConversionStream);
|
// SDL_FreeAudioStream(sConversionStream);
|
||||||
|
|
||||||
SDL_CloseAudio();
|
SDL_CloseAudio();
|
||||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
|
@ -153,9 +157,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||||
restLength = len;
|
restLength = len;
|
||||||
}
|
}
|
||||||
// Mix sample into stream.
|
// Mix sample into stream.
|
||||||
SDL_MixAudioFormat(stream, &(sound->getData()[sound->getPosition()]),
|
SDL_MixAudioFormat(
|
||||||
sAudioFormat.format, restLength, static_cast<int>(Settings::getInstance()->
|
stream, &(sound->getData()[sound->getPosition()]), sAudioFormat.format, restLength,
|
||||||
getInt("SoundVolumeNavigation") * 1.28f));
|
static_cast<int>(Settings::getInstance()->getInt("SoundVolumeNavigation") * 1.28f));
|
||||||
if (sound->getPosition() + restLength < sound->getLength()) {
|
if (sound->getPosition() + restLength < sound->getLength()) {
|
||||||
// Sample hasn't ended yet.
|
// Sample hasn't ended yet.
|
||||||
stillPlaying = true;
|
stillPlaying = true;
|
||||||
|
@ -191,8 +195,8 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||||
|
|
||||||
std::vector<Uint8> converted(chunkLength);
|
std::vector<Uint8> converted(chunkLength);
|
||||||
|
|
||||||
int processedLength = SDL_AudioStreamGet(sConversionStream,
|
int processedLength =
|
||||||
static_cast<void*>(&converted.at(0)), chunkLength);
|
SDL_AudioStreamGet(sConversionStream, static_cast<void*>(&converted.at(0)), chunkLength);
|
||||||
|
|
||||||
if (processedLength < 0) {
|
if (processedLength < 0) {
|
||||||
LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:";
|
LOG(LogError) << "AudioManager::mixAudio(): Couldn't convert sound chunk:";
|
||||||
|
@ -201,9 +205,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable only when needed, as this generates a lot of debug output.
|
// Enable only when needed, as this generates a lot of debug output.
|
||||||
// LOG(LogDebug) << "AudioManager::mixAudio(): chunkLength "
|
// LOG(LogDebug) << "AudioManager::mixAudio(): chunkLength "
|
||||||
// "/ processedLength / streamLength: " << chunkLength << " / " <<
|
// "/ processedLength / streamLength: " << chunkLength << " / " <<
|
||||||
// " / " << processedLength << " / " << streamLength;
|
// " / " << processedLength << " / " << streamLength;
|
||||||
|
|
||||||
// This mute flag is used to make sure that the audio buffer already sent to the
|
// This mute flag is used to make sure that the audio buffer already sent to the
|
||||||
// stream is not played when the video player has been stopped. Otherwise there would
|
// stream is not played when the video player has been stopped. Otherwise there would
|
||||||
|
@ -213,8 +217,9 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||||
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, 0);
|
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
SDL_MixAudioFormat(stream, &converted.at(0), sAudioFormat.format, processedLength,
|
SDL_MixAudioFormat(
|
||||||
static_cast<int>(Settings::getInstance()->getInt("SoundVolumeVideos") * 1.28f));
|
stream, &converted.at(0), sAudioFormat.format, processedLength,
|
||||||
|
static_cast<int>(Settings::getInstance()->getInt("SoundVolumeVideos") * 1.28f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If nothing is playing, pause the device until there is more audio to output.
|
// If nothing is playing, pause the device until there is more audio to output.
|
||||||
|
@ -224,6 +229,7 @@ void AudioManager::mixAudio(void* /*unused*/, Uint8* stream, int len)
|
||||||
|
|
||||||
void AudioManager::registerSound(std::shared_ptr<Sound>& sound)
|
void AudioManager::registerSound(std::shared_ptr<Sound>& sound)
|
||||||
{
|
{
|
||||||
|
// Add sound to sound vector.
|
||||||
sSoundVector.push_back(sound);
|
sSoundVector.push_back(sound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +273,7 @@ void AudioManager::setupAudioStream(int sampleRate)
|
||||||
|
|
||||||
// Used for streaming audio from videos.
|
// Used for streaming audio from videos.
|
||||||
sConversionStream = SDL_NewAudioStream(AUDIO_F32, 2, sampleRate, sAudioFormat.format,
|
sConversionStream = SDL_NewAudioStream(AUDIO_F32, 2, sampleRate, sAudioFormat.format,
|
||||||
sAudioFormat.channels, sAudioFormat.freq);
|
sAudioFormat.channels, sAudioFormat.freq);
|
||||||
if (sConversionStream == nullptr) {
|
if (sConversionStream == nullptr) {
|
||||||
LOG(LogError) << "Failed to create audio conversion stream:";
|
LOG(LogError) << "Failed to create audio conversion stream:";
|
||||||
LOG(LogError) << SDL_GetError();
|
LOG(LogError) << SDL_GetError();
|
||||||
|
@ -298,7 +304,7 @@ void AudioManager::clearStream()
|
||||||
// The SDL_AudioStreamClear() function is unstable and causes random crashes, so
|
// The SDL_AudioStreamClear() function is unstable and causes random crashes, so
|
||||||
// we have to implement a workaround instead where SDL_AudioStreamGet() is used
|
// we have to implement a workaround instead where SDL_AudioStreamGet() is used
|
||||||
// to empty the stream.
|
// to empty the stream.
|
||||||
// SDL_AudioStreamClear(sConversionStream);
|
// SDL_AudioStreamClear(sConversionStream);
|
||||||
|
|
||||||
mIsClearingStream = true;
|
mIsClearingStream = true;
|
||||||
|
|
||||||
|
@ -307,8 +313,8 @@ void AudioManager::clearStream()
|
||||||
|
|
||||||
while ((streamSize = SDL_AudioStreamAvailable(sConversionStream)) > 0) {
|
while ((streamSize = SDL_AudioStreamAvailable(sConversionStream)) > 0) {
|
||||||
std::vector<Uint8> readBuffer(length);
|
std::vector<Uint8> readBuffer(length);
|
||||||
int processedLength = SDL_AudioStreamGet(sConversionStream,
|
int processedLength =
|
||||||
static_cast<void*>(&readBuffer.at(0)), length);
|
SDL_AudioStreamGet(sConversionStream, static_cast<void*>(&readBuffer.at(0)), length);
|
||||||
if (processedLength <= 0) {
|
if (processedLength <= 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,12 @@ extern int SDL_USER_CECBUTTONUP;
|
||||||
CECInput* CECInput::sInstance = nullptr;
|
CECInput* CECInput::sInstance = nullptr;
|
||||||
|
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
static void onAlert(void* /*cbParam*/, const CEC::libcec_alert type,
|
static void onAlert(void* /*cbParam*/,
|
||||||
const CEC::libcec_parameter param)
|
const CEC::libcec_alert type,
|
||||||
|
const CEC::libcec_parameter param)
|
||||||
{
|
{
|
||||||
LOG(LogDebug) << "CECInput::onAlert type: " << CECInput::getAlertTypeString(type) <<
|
LOG(LogDebug) << "CECInput::onAlert type: " << CECInput::getAlertTypeString(type)
|
||||||
" parameter: " << reinterpret_cast<char*>(param.paramData);
|
<< " parameter: " << reinterpret_cast<char*>(param.paramData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void onCommand(void* /*cbParam*/, const CEC::cec_command* command)
|
static void onCommand(void* /*cbParam*/, const CEC::cec_command* command)
|
||||||
|
@ -50,7 +51,7 @@ static void onKeyPress(void* /*cbParam*/, const CEC::cec_keypress* key)
|
||||||
LOG(LogDebug) << "CECInput::onKeyPress keycode: " << CECInput::getKeyCodeString(key->keycode);
|
LOG(LogDebug) << "CECInput::onKeyPress keycode: " << CECInput::getKeyCodeString(key->keycode);
|
||||||
|
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
event.type = (key->duration > 0) ? SDL_USER_CECBUTTONUP : SDL_USER_CECBUTTONDOWN;
|
event.type = (key->duration > 0) ? SDL_USER_CECBUTTONUP : SDL_USER_CECBUTTONDOWN;
|
||||||
event.user.code = key->keycode;
|
event.user.code = key->keycode;
|
||||||
SDL_PushEvent(&event);
|
SDL_PushEvent(&event);
|
||||||
}
|
}
|
||||||
|
@ -93,14 +94,15 @@ void CECInput::deinit()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CECInput::CECInput() : mlibCEC(nullptr)
|
CECInput::CECInput()
|
||||||
|
: mlibCEC(nullptr)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
#if defined(_RPI_)
|
#if defined(_RPI_)
|
||||||
// Restart vchi tv and CEC in case we just came back from another app using CEC (like Kodi).
|
// Restart vchi tv and CEC in case we just came back from another app using CEC (like Kodi).
|
||||||
vchi_tv_and_cec_deinit();
|
vchi_tv_and_cec_deinit();
|
||||||
vchi_tv_and_cec_init();
|
vchi_tv_and_cec_init();
|
||||||
#endif // _RPI_
|
#endif // _RPI_
|
||||||
|
|
||||||
CEC::ICECCallbacks callbacks;
|
CEC::ICECCallbacks callbacks;
|
||||||
CEC::libcec_configuration config;
|
CEC::libcec_configuration config;
|
||||||
|
@ -136,8 +138,8 @@ CECInput::CECInput() : mlibCEC(nullptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < numAdapters; i++)
|
for (int i = 0; i < numAdapters; i++)
|
||||||
LOG(LogDebug) << "CEC adapter: " << i << " path: " << adapters[i].strComPath <<
|
LOG(LogDebug) << "CEC adapter: " << i << " path: " << adapters[i].strComPath
|
||||||
" name: " << adapters[i].strComName;
|
<< " name: " << adapters[i].strComName;
|
||||||
|
|
||||||
if (!mlibCEC->Open(adapters[0].strComName)) {
|
if (!mlibCEC->Open(adapters[0].strComName)) {
|
||||||
LOG(LogError) << "CECInput::mAdapter->Open failed";
|
LOG(LogError) << "CECInput::mAdapter->Open failed";
|
||||||
|
@ -145,28 +147,29 @@ CECInput::CECInput() : mlibCEC(nullptr)
|
||||||
mlibCEC = nullptr;
|
mlibCEC = nullptr;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif // HAVE_LIBCEC
|
#endif // HAVE_LIBCEC
|
||||||
}
|
}
|
||||||
|
|
||||||
CECInput::~CECInput()
|
CECInput::~CECInput()
|
||||||
{
|
{
|
||||||
|
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
if (mlibCEC) {
|
if (mlibCEC) {
|
||||||
mlibCEC->Close();
|
mlibCEC->Close();
|
||||||
UnloadLibCec(mlibCEC);
|
UnloadLibCec(mlibCEC);
|
||||||
mlibCEC = nullptr;
|
mlibCEC = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_RPI_)
|
#if defined(_RPI_)
|
||||||
// Deinit vchi tv and CEC in case we are going to launch another app using CEC (like Kodi).
|
// Deinit vchi tv and CEC in case we are going to launch another app using CEC (like Kodi).
|
||||||
vchi_tv_and_cec_deinit();
|
vchi_tv_and_cec_deinit();
|
||||||
#endif // _RPI_
|
#endif // _RPI_
|
||||||
#endif // HAVE_LIBCEC
|
#endif // HAVE_LIBCEC
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CECInput::getAlertTypeString(const unsigned int _type)
|
std::string CECInput::getAlertTypeString(const unsigned int _type)
|
||||||
{
|
{
|
||||||
|
// clang-format off
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
case CEC::CEC_ALERT_SERVICE_DEVICE: { return "Service-Device"; } break;
|
case CEC::CEC_ALERT_SERVICE_DEVICE: { return "Service-Device"; } break;
|
||||||
|
@ -180,10 +183,12 @@ std::string CECInput::getAlertTypeString(const unsigned int _type)
|
||||||
#endif // HAVE_LIBCEC
|
#endif // HAVE_LIBCEC
|
||||||
default: { return "Unknown"; } break;
|
default: { return "Unknown"; } break;
|
||||||
}
|
}
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CECInput::getOpCodeString(const unsigned int _opCode)
|
std::string CECInput::getOpCodeString(const unsigned int _opCode)
|
||||||
{
|
{
|
||||||
|
// clang-format off
|
||||||
switch (_opCode) {
|
switch (_opCode) {
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
case CEC::CEC_OPCODE_ACTIVE_SOURCE: { return "Active-Source"; } break;
|
case CEC::CEC_OPCODE_ACTIVE_SOURCE: { return "Active-Source"; } break;
|
||||||
|
@ -261,10 +266,12 @@ std::string CECInput::getOpCodeString(const unsigned int _opCode)
|
||||||
#endif // HAVE_LIBCEC
|
#endif // HAVE_LIBCEC
|
||||||
default: { return "Unknown"; } break;
|
default: { return "Unknown"; } break;
|
||||||
}
|
}
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string CECInput::getKeyCodeString(const unsigned int _keyCode)
|
std::string CECInput::getKeyCodeString(const unsigned int _keyCode)
|
||||||
{
|
{
|
||||||
|
// clang-format off
|
||||||
switch (_keyCode) {
|
switch (_keyCode) {
|
||||||
#if defined(HAVE_LIBCEC)
|
#if defined(HAVE_LIBCEC)
|
||||||
case CEC::CEC_USER_CONTROL_CODE_SELECT: { return "Select"; } break;
|
case CEC::CEC_USER_CONTROL_CODE_SELECT: { return "Select"; } break;
|
||||||
|
@ -358,5 +365,6 @@ std::string CECInput::getKeyCodeString(const unsigned int _keyCode)
|
||||||
case 0:
|
case 0:
|
||||||
#endif // HAVE_LIBCEC
|
#endif // HAVE_LIBCEC
|
||||||
default: { return "Unknown"; } break;
|
default: { return "Unknown"; } break;
|
||||||
|
// clang-format off
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -11,7 +11,10 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
namespace CEC { class ICECAdapter; }
|
namespace CEC
|
||||||
|
{
|
||||||
|
class ICECAdapter;
|
||||||
|
}
|
||||||
|
|
||||||
class CECInput
|
class CECInput
|
||||||
{
|
{
|
||||||
|
@ -23,7 +26,7 @@ public:
|
||||||
static std::string getKeyCodeString(const unsigned int _keyCode);
|
static std::string getKeyCodeString(const unsigned int _keyCode);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CECInput();
|
CECInput();
|
||||||
~CECInput();
|
~CECInput();
|
||||||
|
|
||||||
static CECInput* sInstance;
|
static CECInput* sInstance;
|
||||||
|
|
|
@ -8,31 +8,30 @@
|
||||||
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
|
||||||
#include "animations/Animation.h"
|
|
||||||
#include "animations/AnimationController.h"
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "animations/Animation.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
GuiComponent::GuiComponent(Window* window)
|
GuiComponent::GuiComponent(Window* window)
|
||||||
: mWindow(window),
|
: mWindow(window)
|
||||||
mParent(nullptr),
|
, mParent(nullptr)
|
||||||
mColor(0),
|
, mColor(0)
|
||||||
mColorShift(0),
|
, mColorShift(0)
|
||||||
mColorShiftEnd(0),
|
, mColorShiftEnd(0)
|
||||||
mOpacity(255),
|
, mOpacity(255)
|
||||||
mSaturation(1.0),
|
, mSaturation(1.0f)
|
||||||
mPosition(Vector3f::Zero()),
|
, mPosition(Vector3f::Zero())
|
||||||
mOrigin(Vector2f::Zero()),
|
, mOrigin(Vector2f::Zero())
|
||||||
mRotationOrigin(0.5, 0.5),
|
, mRotationOrigin(0.5f, 0.5f)
|
||||||
mSize(Vector2f::Zero()),
|
, mSize(Vector2f::Zero())
|
||||||
mTransform(Transform4x4f::Identity()),
|
, mTransform(Transform4x4f::Identity())
|
||||||
mIsProcessing(false),
|
, mIsProcessing(false)
|
||||||
mVisible(true),
|
, mVisible(true)
|
||||||
mEnabled(true)
|
, mEnabled(true)
|
||||||
{
|
{
|
||||||
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
for (unsigned char i = 0; i < MAX_ANIMATIONS; i++)
|
||||||
mAnimationMap[i] = nullptr;
|
mAnimationMap[i] = nullptr;
|
||||||
|
@ -94,105 +93,30 @@ void GuiComponent::renderChildren(const Transform4x4f& transform) const
|
||||||
getChild(i)->render(transform);
|
getChild(i)->render(transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f GuiComponent::getPosition() const
|
|
||||||
{
|
|
||||||
return mPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setPosition(float x, float y, float z)
|
void GuiComponent::setPosition(float x, float y, float z)
|
||||||
{
|
{
|
||||||
mPosition = Vector3f(x, y, z);
|
mPosition = Vector3f(x, y, z);
|
||||||
onPositionChanged();
|
onPositionChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f GuiComponent::getOrigin() const
|
|
||||||
{
|
|
||||||
return mOrigin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setOrigin(float x, float y)
|
void GuiComponent::setOrigin(float x, float y)
|
||||||
{
|
{
|
||||||
mOrigin = Vector2f(x, y);
|
mOrigin = Vector2f(x, y);
|
||||||
onOriginChanged();
|
onOriginChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f GuiComponent::getRotationOrigin() const
|
|
||||||
{
|
|
||||||
return mRotationOrigin;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setRotationOrigin(float x, float y)
|
|
||||||
{
|
|
||||||
mRotationOrigin = Vector2f(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2f GuiComponent::getSize() const
|
|
||||||
{
|
|
||||||
return mSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setSize(float w, float h)
|
void GuiComponent::setSize(float w, float h)
|
||||||
{
|
{
|
||||||
mSize = Vector2f(w, h);
|
mSize = Vector2f(w, h);
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
float GuiComponent::getRotation() const
|
|
||||||
{
|
|
||||||
return mRotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setRotation(float rotation)
|
|
||||||
{
|
|
||||||
mRotation = rotation;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GuiComponent::getScale() const
|
|
||||||
{
|
|
||||||
return mScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setScale(float scale)
|
|
||||||
{
|
|
||||||
mScale = scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GuiComponent::getZIndex() const
|
|
||||||
{
|
|
||||||
return mZIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setZIndex(float z)
|
|
||||||
{
|
|
||||||
mZIndex = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GuiComponent::getDefaultZIndex() const
|
|
||||||
{
|
|
||||||
return mDefaultZIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setDefaultZIndex(float z)
|
|
||||||
{
|
|
||||||
mDefaultZIndex = z;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GuiComponent::isVisible() const
|
|
||||||
{
|
|
||||||
return mVisible;
|
|
||||||
}
|
|
||||||
void GuiComponent::setVisible(bool visible)
|
|
||||||
{
|
|
||||||
mVisible = visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2f GuiComponent::getCenter() const
|
Vector2f GuiComponent::getCenter() const
|
||||||
{
|
{
|
||||||
return Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2,
|
return Vector2f(mPosition.x() - (getSize().x() * mOrigin.x()) + getSize().x() / 2.0f,
|
||||||
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2);
|
mPosition.y() - (getSize().y() * mOrigin.y()) + getSize().y() / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Children stuff.
|
|
||||||
void GuiComponent::addChild(GuiComponent* cmp)
|
void GuiComponent::addChild(GuiComponent* cmp)
|
||||||
{
|
{
|
||||||
mChildren.push_back(cmp);
|
mChildren.push_back(cmp);
|
||||||
|
@ -222,11 +146,6 @@ void GuiComponent::removeChild(GuiComponent* cmp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiComponent::clearChildren()
|
|
||||||
{
|
|
||||||
mChildren.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -234,15 +153,10 @@ void GuiComponent::sortChildren()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GuiComponent::getChildCount() const
|
|
||||||
{
|
|
||||||
return static_cast<int>(mChildren.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
int GuiComponent::getChildIndex() const
|
int GuiComponent::getChildIndex() const
|
||||||
{
|
{
|
||||||
std::vector<GuiComponent*>::iterator it =
|
std::vector<GuiComponent*>::iterator it =
|
||||||
std::find(getParent()->mChildren.begin(), getParent()->mChildren.end(), this);
|
std::find(getParent()->mChildren.begin(), getParent()->mChildren.end(), this);
|
||||||
|
|
||||||
if (it != getParent()->mChildren.end())
|
if (it != getParent()->mChildren.end())
|
||||||
return static_cast<int>(std::distance(getParent()->mChildren.begin(), it));
|
return static_cast<int>(std::distance(getParent()->mChildren.begin(), it));
|
||||||
|
@ -250,26 +164,6 @@ int GuiComponent::getChildIndex() const
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiComponent* GuiComponent::getChild(unsigned int i) const
|
|
||||||
{
|
|
||||||
return mChildren.at(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setParent(GuiComponent* parent)
|
|
||||||
{
|
|
||||||
mParent = parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
GuiComponent* GuiComponent::getParent() const
|
|
||||||
{
|
|
||||||
return mParent;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned char GuiComponent::getOpacity() const
|
|
||||||
{
|
|
||||||
return mOpacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setOpacity(unsigned char opacity)
|
void GuiComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
mOpacity = opacity;
|
mOpacity = opacity;
|
||||||
|
@ -277,92 +171,47 @@ void GuiComponent::setOpacity(unsigned char opacity)
|
||||||
(*it)->setOpacity(opacity);
|
(*it)->setOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int GuiComponent::getColor() const
|
|
||||||
{
|
|
||||||
return mColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int GuiComponent::getColorShift() const
|
|
||||||
{
|
|
||||||
return mColorShift;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setColor(unsigned int color)
|
|
||||||
{
|
|
||||||
mColor = color;
|
|
||||||
mColorOpacity = mColor & 0x000000FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GuiComponent::getSaturation() const
|
|
||||||
{
|
|
||||||
return static_cast<float>(mColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setSaturation(float saturation)
|
|
||||||
{
|
|
||||||
mSaturation = saturation;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setColorShift(unsigned int color)
|
|
||||||
{
|
|
||||||
mColorShift = color;
|
|
||||||
mColorShiftEnd = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Transform4x4f& GuiComponent::getTransform()
|
const Transform4x4f& GuiComponent::getTransform()
|
||||||
{
|
{
|
||||||
mTransform = Transform4x4f::Identity();
|
mTransform = Transform4x4f::Identity();
|
||||||
mTransform.translate(mPosition);
|
mTransform.translate(mPosition);
|
||||||
if (mScale != 1.0)
|
|
||||||
|
if (mScale != 1.0f)
|
||||||
mTransform.scale(mScale);
|
mTransform.scale(mScale);
|
||||||
if (mRotation != 0.0) {
|
|
||||||
|
if (mRotation != 0.0f) {
|
||||||
// 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.0f || yOff != 0.0f)
|
||||||
mTransform.translate(Vector3f(xOff * -1, yOff * -1, 0.0f));
|
mTransform.translate(Vector3f(xOff * -1.0f, yOff * -1.0f, 0.0f));
|
||||||
|
|
||||||
// Apply rotation transform.
|
// Apply rotation transform.
|
||||||
mTransform.rotateZ(mRotation);
|
mTransform.rotateZ(mRotation);
|
||||||
|
|
||||||
// Transform back to original point.
|
// Transform back to original point.
|
||||||
if (xOff != 0.0 || yOff != 0.0)
|
if (xOff != 0.0f || yOff != 0.0f)
|
||||||
mTransform.translate(Vector3f(xOff, yOff, 0.0f));
|
mTransform.translate(Vector3f(xOff, yOff, 0.0f));
|
||||||
}
|
}
|
||||||
mTransform.translate(Vector3f(mOrigin.x() * mSize.x() * -1,
|
mTransform.translate(
|
||||||
mOrigin.y() * mSize.y() * -1, 0.0f));
|
Vector3f(mOrigin.x() * mSize.x() * -1.0f, mOrigin.y() * mSize.y() * -1.0f, 0.0f));
|
||||||
return mTransform;
|
return mTransform;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GuiComponent::getValue() const
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setValue(const std::string& /*value*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string GuiComponent::getHiddenValue() const
|
|
||||||
{
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::setHiddenValue(const std::string& /*value*/)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::textInput(const std::string& text)
|
void GuiComponent::textInput(const std::string& 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,
|
void GuiComponent::setAnimation(Animation* anim,
|
||||||
std::function<void()> finishedCallback, bool reverse, unsigned char slot)
|
int delay,
|
||||||
|
std::function<void()> finishedCallback,
|
||||||
|
bool reverse,
|
||||||
|
unsigned char slot)
|
||||||
{
|
{
|
||||||
assert(slot < MAX_ANIMATIONS);
|
assert(slot < MAX_ANIMATIONS);
|
||||||
|
|
||||||
|
@ -447,29 +296,14 @@ void GuiComponent::cancelAllAnimations()
|
||||||
cancelAnimation(i);
|
cancelAnimation(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiComponent::isAnimationPlaying(unsigned char slot) const
|
|
||||||
{
|
|
||||||
return mAnimationMap[slot] != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GuiComponent::isAnimationReversed(unsigned char slot) const
|
|
||||||
{
|
|
||||||
assert(mAnimationMap[slot] != nullptr);
|
|
||||||
return mAnimationMap[slot]->isReversed();
|
|
||||||
}
|
|
||||||
|
|
||||||
int GuiComponent::getAnimationTime(unsigned char slot) const
|
|
||||||
{
|
|
||||||
assert(mAnimationMap[slot] != nullptr);
|
|
||||||
return mAnimationMap[slot]->getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
Vector2f scale = getParent() ? getParent()->getSize()
|
Vector2f scale = getParent() ? getParent()->getSize() :
|
||||||
: Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "");
|
||||||
if (!elem)
|
if (!elem)
|
||||||
|
@ -484,9 +318,9 @@ void GuiComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
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 &&
|
if ((properties & ORIGIN || (properties & POSITION && properties & ThemeFlags::SIZE)) &&
|
||||||
properties & ThemeFlags::SIZE)) && elem->has("origin")) {
|
elem->has("origin")) {
|
||||||
setOrigin(elem->get<Vector2f>("origin"));
|
setOrigin(elem->get<Vector2f>("origin"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,16 +355,6 @@ void GuiComponent::updateHelpPrompts()
|
||||||
mWindow->setHelpPrompts(prompts, getHelpStyle());
|
mWindow->setHelpPrompts(prompts, getHelpStyle());
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle GuiComponent::getHelpStyle()
|
|
||||||
{
|
|
||||||
return HelpStyle();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GuiComponent::isProcessing() const
|
|
||||||
{
|
|
||||||
return mIsProcessing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GuiComponent::onShow()
|
void GuiComponent::onShow()
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < getChildCount(); i++)
|
for (unsigned int i = 0; i < getChildCount(); i++)
|
||||||
|
|
|
@ -9,11 +9,12 @@
|
||||||
#ifndef ES_CORE_GUI_COMPONENT_H
|
#ifndef ES_CORE_GUI_COMPONENT_H
|
||||||
#define ES_CORE_GUI_COMPONENT_H
|
#define ES_CORE_GUI_COMPONENT_H
|
||||||
|
|
||||||
#include "math/Misc.h"
|
|
||||||
#include "math/Transform4x4f.h"
|
|
||||||
#include "HelpPrompt.h"
|
#include "HelpPrompt.h"
|
||||||
#include "HelpStyle.h"
|
#include "HelpStyle.h"
|
||||||
#include "InputConfig.h"
|
#include "InputConfig.h"
|
||||||
|
#include "animations/AnimationController.h"
|
||||||
|
#include "math/Misc.h"
|
||||||
|
#include "math/Transform4x4f.h"
|
||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
@ -65,72 +66,84 @@ public:
|
||||||
// 4. Tell your children to render, based on your component's transform - renderChildren(t).
|
// 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 { return mPosition; }
|
||||||
inline void setPosition(const Vector3f& offset)
|
void setPosition(const Vector3f& offset) { setPosition(offset.x(), offset.y(), offset.z()); }
|
||||||
{ 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() {}
|
||||||
|
|
||||||
|
Vector2f getOrigin() const { return mOrigin; }
|
||||||
// Sets the origin as a percentage of this image.
|
// Sets the origin as a percentage of this image.
|
||||||
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
|
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
|
||||||
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()); }
|
void setOrigin(Vector2f origin) { setOrigin(origin.x(), origin.y()); }
|
||||||
virtual void onOriginChanged() {};
|
virtual void onOriginChanged() {}
|
||||||
|
|
||||||
|
Vector2f getRotationOrigin() const { return mRotationOrigin; }
|
||||||
// Sets the rotation origin as a percentage of this image.
|
// Sets the rotation origin as a percentage of this image.
|
||||||
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
|
// (e.g. (0, 0) is top left, (0.5, 0.5) is the center.)
|
||||||
Vector2f getRotationOrigin() const;
|
void setRotationOrigin(float originX, float originY)
|
||||||
void setRotationOrigin(float originX, float originY);
|
{
|
||||||
inline void setRotationOrigin(Vector2f origin)
|
mRotationOrigin = Vector2f(originX, originY);
|
||||||
{ setRotationOrigin(origin.x(), origin.y()); }
|
}
|
||||||
|
void setRotationOrigin(Vector2f origin) { setRotationOrigin(origin.x(), origin.y()); }
|
||||||
|
|
||||||
virtual Vector2f getSize() const;
|
virtual Vector2f getSize() const { return mSize; }
|
||||||
inline void setSize(const Vector2f& size) { setSize(size.x(), size.y()); }
|
void setSize(const Vector2f& size) { setSize(size.x(), size.y()); }
|
||||||
void setSize(float w, float h);
|
void setSize(float w, float h);
|
||||||
virtual void setResize(float width, float height) {};
|
virtual void setResize(float width, float height) {}
|
||||||
virtual void onSizeChanged() {};
|
virtual void onSizeChanged() {}
|
||||||
|
|
||||||
virtual Vector2f getRotationSize() const { return getSize(); };
|
virtual Vector2f getRotationSize() const { return getSize(); }
|
||||||
|
float getRotation() const { return mRotation; }
|
||||||
|
void setRotation(float rotation) { mRotation = rotation; }
|
||||||
|
void setRotationDegrees(float rotation)
|
||||||
|
{
|
||||||
|
setRotation(static_cast<float>(ES_DEG_TO_RAD(rotation)));
|
||||||
|
}
|
||||||
|
|
||||||
float getRotation() const;
|
float getScale() const { return mScale; }
|
||||||
void setRotation(float rotation);
|
void setScale(float scale) { mScale = scale; }
|
||||||
inline void setRotationDegrees(float rotation) {
|
|
||||||
setRotation(static_cast<float>(ES_DEG_TO_RAD(rotation))); }
|
|
||||||
|
|
||||||
float getScale() const;
|
float getZIndex() const { return mZIndex; }
|
||||||
void setScale(float scale);
|
void setZIndex(float zIndex) { mZIndex = zIndex; }
|
||||||
|
|
||||||
float getZIndex() const;
|
float getDefaultZIndex() const { return mDefaultZIndex; }
|
||||||
void setZIndex(float zIndex);
|
void setDefaultZIndex(float zIndex) { mDefaultZIndex = zIndex; }
|
||||||
|
|
||||||
float getDefaultZIndex() const;
|
bool isVisible() const { return mVisible; }
|
||||||
void setDefaultZIndex(float zIndex);
|
void setVisible(bool visible) { mVisible = visible; }
|
||||||
|
|
||||||
bool isVisible() const;
|
|
||||||
void setVisible(bool visible);
|
|
||||||
|
|
||||||
// Returns the center point of the image (takes origin into account).
|
// Returns the center point of the image (takes origin into account).
|
||||||
Vector2f getCenter() const;
|
Vector2f getCenter() const;
|
||||||
|
|
||||||
void setParent(GuiComponent* parent);
|
void setParent(GuiComponent* parent) { mParent = parent; }
|
||||||
GuiComponent* getParent() const;
|
GuiComponent* getParent() const { return mParent; }
|
||||||
|
|
||||||
void addChild(GuiComponent* cmp);
|
void addChild(GuiComponent* cmp);
|
||||||
void removeChild(GuiComponent* cmp);
|
void removeChild(GuiComponent* cmp);
|
||||||
void clearChildren();
|
void clearChildren() { mChildren.clear(); }
|
||||||
void sortChildren();
|
void sortChildren();
|
||||||
unsigned int getChildCount() const;
|
unsigned int getChildCount() const { return static_cast<int>(mChildren.size()); }
|
||||||
int getChildIndex() const;
|
int getChildIndex() const;
|
||||||
GuiComponent* getChild(unsigned int i) const;
|
GuiComponent* getChild(unsigned int i) const { return mChildren.at(i); }
|
||||||
|
|
||||||
// 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 { return mAnimationMap[slot] != nullptr; }
|
||||||
bool isAnimationReversed(unsigned char slot) const;
|
bool isAnimationReversed(unsigned char slot) const
|
||||||
int getAnimationTime(unsigned char slot) const;
|
{
|
||||||
void setAnimation(Animation* animation, int delay = 0,
|
assert(mAnimationMap[slot] != nullptr);
|
||||||
std::function<void()> finishedCallback = nullptr,
|
return mAnimationMap[slot]->isReversed();
|
||||||
bool reverse = false, unsigned char slot = 0);
|
}
|
||||||
|
int getAnimationTime(unsigned char slot) const
|
||||||
|
{
|
||||||
|
assert(mAnimationMap[slot] != nullptr);
|
||||||
|
return mAnimationMap[slot]->getTime();
|
||||||
|
}
|
||||||
|
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);
|
||||||
// Like stopAnimation, but doesn't call finishedCallback - only removes the animation, leaving
|
// 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).
|
// things in their current state. Returns true if successful (an animation was in this slot).
|
||||||
|
@ -143,45 +156,53 @@ public:
|
||||||
void stopAllAnimations();
|
void stopAllAnimations();
|
||||||
void cancelAllAnimations();
|
void cancelAllAnimations();
|
||||||
|
|
||||||
virtual bool isListScrolling() { return false; };
|
virtual bool isListScrolling() { return false; }
|
||||||
virtual void stopListScrolling() {};
|
virtual void stopListScrolling() {}
|
||||||
virtual unsigned char getOpacity() const;
|
virtual unsigned char getOpacity() const { return mOpacity; }
|
||||||
virtual void setOpacity(unsigned char opacity);
|
virtual void setOpacity(unsigned char opacity);
|
||||||
virtual unsigned int getColor() const;
|
virtual unsigned int getColor() const { return mColor; }
|
||||||
virtual unsigned int getColorShift() const;
|
virtual unsigned int getColorShift() const { return mColorShift; }
|
||||||
virtual void setColor(unsigned int color);
|
virtual void setColor(unsigned int color)
|
||||||
virtual float getSaturation() const;
|
{
|
||||||
virtual void setSaturation(float saturation);
|
mColor = color;
|
||||||
virtual void setColorShift(unsigned int color);
|
mColorOpacity = mColor & 0x000000FF;
|
||||||
virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; };
|
}
|
||||||
virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; };
|
virtual float getSaturation() const { return static_cast<float>(mColor); }
|
||||||
|
virtual void setSaturation(float saturation) { mSaturation = saturation; }
|
||||||
|
virtual void setColorShift(unsigned int color)
|
||||||
|
{
|
||||||
|
mColorShift = color;
|
||||||
|
mColorShiftEnd = color;
|
||||||
|
}
|
||||||
|
virtual void setOriginalColor(unsigned int color) { mColorOriginalValue = color; }
|
||||||
|
virtual void setChangedColor(unsigned int color) { mColorChangedValue = color; }
|
||||||
|
|
||||||
// These functions are used to enable and disable options in menus, i.e. switches and similar.
|
// These functions are used to enable and disable options in menus, i.e. switches and similar.
|
||||||
virtual bool getEnabled() { return mEnabled; };
|
virtual bool getEnabled() { return mEnabled; }
|
||||||
virtual void setEnabled(bool state) { mEnabled = state; };
|
virtual void setEnabled(bool state) { mEnabled = state; }
|
||||||
|
|
||||||
virtual std::shared_ptr<Font> getFont() const { return nullptr; };
|
virtual std::shared_ptr<Font> getFont() const { return nullptr; }
|
||||||
|
|
||||||
const Transform4x4f& getTransform();
|
const Transform4x4f& getTransform();
|
||||||
|
|
||||||
virtual std::string getValue() const;
|
virtual std::string getValue() const { return ""; }
|
||||||
virtual void setValue(const std::string& value);
|
virtual void setValue(const std::string& value) {}
|
||||||
|
|
||||||
virtual std::string getHiddenValue() const;
|
virtual std::string getHiddenValue() const { return ""; }
|
||||||
virtual void setHiddenValue(const std::string& value);
|
virtual void setHiddenValue(const std::string& value) {}
|
||||||
|
|
||||||
// Used to set the parameters for ScrollableContainer.
|
// Used to set the parameters for ScrollableContainer.
|
||||||
virtual void setScrollParameters(float, float, int) {};
|
virtual void setScrollParameters(float, float, int) {}
|
||||||
|
|
||||||
virtual void onFocusGained() {};
|
virtual void onFocusGained() {}
|
||||||
virtual void onFocusLost() {};
|
virtual void onFocusLost() {}
|
||||||
|
|
||||||
virtual void onShow();
|
virtual void onShow();
|
||||||
virtual void onHide();
|
virtual void onHide();
|
||||||
virtual void onStopVideo();
|
virtual void onStopVideo();
|
||||||
virtual void onPauseVideo();
|
virtual void onPauseVideo();
|
||||||
virtual void onUnpauseVideo();
|
virtual void onUnpauseVideo();
|
||||||
virtual bool isVideoPaused() { return false; };
|
virtual bool isVideoPaused() { return false; }
|
||||||
|
|
||||||
virtual void onScreensaverActivate();
|
virtual void onScreensaverActivate();
|
||||||
virtual void onScreensaverDeactivate();
|
virtual void onScreensaverDeactivate();
|
||||||
|
@ -192,18 +213,20 @@ 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,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties);
|
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>(); }
|
||||||
|
|
||||||
// Called whenever help prompts change.
|
// Called whenever help prompts change.
|
||||||
void updateHelpPrompts();
|
void updateHelpPrompts();
|
||||||
|
|
||||||
virtual HelpStyle getHelpStyle();
|
virtual HelpStyle getHelpStyle() { return HelpStyle(); }
|
||||||
|
|
||||||
// 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 { return mIsProcessing; }
|
||||||
|
|
||||||
const static unsigned char MAX_ANIMATIONS = 4;
|
const static unsigned char MAX_ANIMATIONS = 4;
|
||||||
|
|
||||||
|
|
|
@ -31,9 +31,9 @@ void HelpStyle::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::s
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (elem->has("pos"))
|
if (elem->has("pos"))
|
||||||
position = elem->get<Vector2f>("pos") *
|
position =
|
||||||
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
elem->get<Vector2f>("pos") * Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
if (elem->has("origin"))
|
if (elem->has("origin"))
|
||||||
origin = elem->get<Vector2f>("origin");
|
origin = elem->get<Vector2f>("origin");
|
||||||
|
|
|
@ -10,21 +10,21 @@
|
||||||
|
|
||||||
#include "HttpReq.h"
|
#include "HttpReq.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
CURLM* HttpReq::s_multi_handle;
|
CURLM* HttpReq::s_multi_handle;
|
||||||
std::map<CURL*, HttpReq*> HttpReq::s_requests;
|
std::map<CURL*, HttpReq*> HttpReq::s_requests;
|
||||||
|
|
||||||
std::string HttpReq::urlEncode(const std::string &s)
|
std::string HttpReq::urlEncode(const std::string& s)
|
||||||
{
|
{
|
||||||
const std::string unreserved =
|
const std::string unreserved =
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~";
|
||||||
|
|
||||||
std::string escaped="";
|
std::string escaped = "";
|
||||||
for (size_t i = 0; i < s.length(); i++) {
|
for (size_t i = 0; i < s.length(); i++) {
|
||||||
if (unreserved.find_first_of(s[i]) != std::string::npos) {
|
if (unreserved.find_first_of(s[i]) != std::string::npos) {
|
||||||
escaped.push_back(s[i]);
|
escaped.push_back(s[i]);
|
||||||
|
@ -43,11 +43,13 @@ bool HttpReq::isUrl(const std::string& str)
|
||||||
{
|
{
|
||||||
// The worst guess.
|
// The worst guess.
|
||||||
return (!str.empty() && !Utils::FileSystem::exists(str) &&
|
return (!str.empty() && !Utils::FileSystem::exists(str) &&
|
||||||
(str.find("http://") != std::string::npos || str.find("https://") !=
|
(str.find("http://") != std::string::npos ||
|
||||||
std::string::npos || str.find("www.") != std::string::npos));
|
str.find("https://") != std::string::npos || str.find("www.") != std::string::npos));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nullptr)
|
HttpReq::HttpReq(const std::string& url)
|
||||||
|
: mStatus(REQ_IN_PROGRESS)
|
||||||
|
, mHandle(nullptr)
|
||||||
{
|
{
|
||||||
// The multi-handle is cleaned up via a call from GuiScraperSearch after the scraping
|
// The multi-handle is cleaned up via a call from GuiScraperSearch after the scraping
|
||||||
// has been completed for a game, meaning the handle is valid for all cURL requests
|
// has been completed for a game, meaning the handle is valid for all cURL requests
|
||||||
|
@ -57,14 +59,16 @@ HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nul
|
||||||
|
|
||||||
mHandle = curl_easy_init();
|
mHandle = curl_easy_init();
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
// On Windows, use the bundled cURL TLS/SSL certificates (which actually come from the
|
// On Windows, use the bundled cURL TLS/SSL certificates (which actually come from the
|
||||||
// Mozilla project). There is a possibility to use the OS provided Schannel certificates
|
// Mozilla project). There is a possibility to use the OS provided Schannel certificates
|
||||||
// but I haven't been able to get this to work and it also seems to be problematic on
|
// but I haven't been able to get this to work and it also seems to be problematic on
|
||||||
// older Windows versions.
|
// older Windows versions.
|
||||||
#if defined(_WIN64)
|
curl_easy_setopt(mHandle, CURLOPT_CAINFO,
|
||||||
curl_easy_setopt(mHandle, CURLOPT_CAINFO, ResourceManager::getInstance()->
|
ResourceManager::getInstance()
|
||||||
getResourcePath(":/certificates/curl-ca-bundle.crt").c_str());
|
->getResourcePath(":/certificates/curl-ca-bundle.crt")
|
||||||
#endif
|
.c_str());
|
||||||
|
#endif
|
||||||
|
|
||||||
if (mHandle == nullptr) {
|
if (mHandle == nullptr) {
|
||||||
mStatus = REQ_IO_ERROR;
|
mStatus = REQ_IO_ERROR;
|
||||||
|
@ -140,8 +144,8 @@ HttpReq::~HttpReq()
|
||||||
CURLMcode merr = curl_multi_remove_handle(s_multi_handle, mHandle);
|
CURLMcode merr = curl_multi_remove_handle(s_multi_handle, mHandle);
|
||||||
|
|
||||||
if (merr != CURLM_OK) {
|
if (merr != CURLM_OK) {
|
||||||
LOG(LogError) << "Error removing curl_easy handle from curl_multi: " <<
|
LOG(LogError) << "Error removing curl_easy handle from curl_multi: "
|
||||||
curl_multi_strerror(merr);
|
<< curl_multi_strerror(merr);
|
||||||
}
|
}
|
||||||
|
|
||||||
curl_easy_cleanup(mHandle);
|
curl_easy_cleanup(mHandle);
|
||||||
|
@ -194,16 +198,6 @@ std::string HttpReq::getContent() const
|
||||||
return mContent.str();
|
return mContent.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HttpReq::onError(const std::string& msg)
|
|
||||||
{
|
|
||||||
mErrorMsg = msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string HttpReq::getErrorMsg()
|
|
||||||
{
|
|
||||||
return mErrorMsg;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used as a curl callback.
|
// Used as a curl callback.
|
||||||
// size = size of an element, nmemb = number of elements.
|
// size = size of an element, nmemb = number of elements.
|
||||||
// Return value is number of elements successfully read.
|
// Return value is number of elements successfully read.
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
~HttpReq();
|
~HttpReq();
|
||||||
|
|
||||||
enum Status {
|
enum Status {
|
||||||
|
// clang-format off
|
||||||
REQ_IN_PROGRESS, // Request is in progress.
|
REQ_IN_PROGRESS, // Request is in progress.
|
||||||
REQ_SUCCESS, // Request completed successfully, get it with getContent().
|
REQ_SUCCESS, // Request completed successfully, get it with getContent().
|
||||||
REQ_IO_ERROR, // Some error happened, get it with getErrorMsg().
|
REQ_IO_ERROR, // Some error happened, get it with getErrorMsg().
|
||||||
|
@ -50,13 +51,14 @@ public:
|
||||||
REQ_BAD_STATUS_CODE, // Some invalid HTTP response status code happened (non-200).
|
REQ_BAD_STATUS_CODE, // Some invalid HTTP response status code happened (non-200).
|
||||||
REQ_INVALID_RESPONSE, // The HTTP response was invalid.
|
REQ_INVALID_RESPONSE, // The HTTP response was invalid.
|
||||||
REQ_UNDEFINED_ERROR
|
REQ_UNDEFINED_ERROR
|
||||||
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
Status status(); // Process any received data and return the status afterwards.
|
Status status(); // Process any received data and return the status afterwards.
|
||||||
std::string getErrorMsg();
|
std::string getErrorMsg() { return mErrorMsg; }
|
||||||
std::string getContent() const; // mStatus must be REQ_SUCCESS.
|
std::string getContent() const; // mStatus must be REQ_SUCCESS.
|
||||||
|
|
||||||
static std::string urlEncode(const std::string &s);
|
static std::string urlEncode(const std::string& s);
|
||||||
static bool isUrl(const std::string& s);
|
static bool isUrl(const std::string& s);
|
||||||
|
|
||||||
static void cleanupCurlMulti()
|
static void cleanupCurlMulti()
|
||||||
|
@ -65,11 +67,11 @@ public:
|
||||||
curl_multi_cleanup(s_multi_handle);
|
curl_multi_cleanup(s_multi_handle);
|
||||||
s_multi_handle = nullptr;
|
s_multi_handle = nullptr;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static size_t write_content(void* buff, size_t size, size_t nmemb, void* req_ptr);
|
static size_t write_content(void* buff, size_t size, size_t nmemb, void* req_ptr);
|
||||||
void onError(const std::string& msg);
|
void onError(const std::string& msg) { mErrorMsg = msg; }
|
||||||
|
|
||||||
// God dammit libcurl why can't you have some way to check the status of an
|
// God dammit libcurl why can't you have some way to check the status of an
|
||||||
// individual handle why do I have to handle ALL messages at once.
|
// individual handle why do I have to handle ALL messages at once.
|
||||||
|
|
|
@ -14,7 +14,9 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* data,
|
std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* data,
|
||||||
const size_t size, size_t& width, size_t& height)
|
const size_t size,
|
||||||
|
size_t& width,
|
||||||
|
size_t& height)
|
||||||
{
|
{
|
||||||
std::vector<unsigned char> rawData;
|
std::vector<unsigned char> rawData;
|
||||||
width = 0;
|
width = 0;
|
||||||
|
@ -44,8 +46,7 @@ std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* da
|
||||||
// This is necessary, because width*height*bpp might not be == pitch.
|
// This is necessary, because width*height*bpp might not be == pitch.
|
||||||
unsigned char* tempData = new unsigned char[width * height * 4];
|
unsigned char* tempData = new unsigned char[width * height * 4];
|
||||||
for (size_t i = 0; i < height; i++) {
|
for (size_t i = 0; i < height; i++) {
|
||||||
const BYTE* scanLine =
|
const BYTE* scanLine = FreeImage_GetScanLine(fiBitmap, static_cast<int>(i));
|
||||||
FreeImage_GetScanLine(fiBitmap, static_cast<int>(i));
|
|
||||||
memcpy(tempData + (i * width * 4), scanLine, width * 4);
|
memcpy(tempData + (i * width * 4), scanLine, width * 4);
|
||||||
}
|
}
|
||||||
// Convert from BGRA to RGBA.
|
// Convert from BGRA to RGBA.
|
||||||
|
@ -69,8 +70,8 @@ std::vector<unsigned char> ImageIO::loadFromMemoryRGBA32(const unsigned char* da
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogError) << "Couldn't load image, file is missing or the file type is " <<
|
LOG(LogError) << "Couldn't load image, file is missing or the file type is "
|
||||||
(format == FIF_UNKNOWN ? "unknown" : "unsupported");
|
<< (format == FIF_UNKNOWN ? "unknown" : "unsupported");
|
||||||
}
|
}
|
||||||
// Free fiMemory again.
|
// Free fiMemory again.
|
||||||
FreeImage_CloseMemory(fiMemory);
|
FreeImage_CloseMemory(fiMemory);
|
||||||
|
|
|
@ -16,7 +16,9 @@ class ImageIO
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::vector<unsigned char> loadFromMemoryRGBA32(const unsigned char* data,
|
static std::vector<unsigned char> loadFromMemoryRGBA32(const unsigned char* data,
|
||||||
const size_t size, size_t& width, size_t& height);
|
const size_t size,
|
||||||
|
size_t& width,
|
||||||
|
size_t& height);
|
||||||
static void flipPixelsVert(unsigned char* imagePx, const size_t& width, const size_t& height);
|
static void flipPixelsVert(unsigned char* imagePx, const size_t& width, const size_t& height);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -12,13 +12,10 @@
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
InputConfig::InputConfig(
|
InputConfig::InputConfig(int deviceId, const std::string& deviceName, const std::string& deviceGUID)
|
||||||
int deviceId,
|
: mDeviceId(deviceId)
|
||||||
const std::string& deviceName,
|
, mDeviceName(deviceName)
|
||||||
const std::string& deviceGUID)
|
, mDeviceGUID(deviceGUID)
|
||||||
: mDeviceId(deviceId),
|
|
||||||
mDeviceName(deviceName),
|
|
||||||
mDeviceGUID(deviceGUID)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +56,6 @@ std::string InputConfig::toLower(std::string str)
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputConfig::clear()
|
|
||||||
{
|
|
||||||
mNameMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InputConfig::isConfigured()
|
|
||||||
{
|
|
||||||
return mNameMap.size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InputConfig::mapInput(const std::string& name, Input input)
|
void InputConfig::mapInput(const std::string& name, Input input)
|
||||||
{
|
{
|
||||||
mNameMap[toLower(name)] = input;
|
mNameMap[toLower(name)] = input;
|
||||||
|
@ -100,19 +87,19 @@ bool InputConfig::isMappedLike(const std::string& name, Input input)
|
||||||
{
|
{
|
||||||
if (name == "left") {
|
if (name == "left") {
|
||||||
return isMappedTo("left", input) || isMappedTo("leftthumbstickleft", input) ||
|
return isMappedTo("left", input) || isMappedTo("leftthumbstickleft", input) ||
|
||||||
isMappedTo("rightthumbstickleft", input);
|
isMappedTo("rightthumbstickleft", input);
|
||||||
}
|
}
|
||||||
else if (name == "right") {
|
else if (name == "right") {
|
||||||
return isMappedTo("right", input) || isMappedTo("leftthumbstickright", input) ||
|
return isMappedTo("right", input) || isMappedTo("leftthumbstickright", input) ||
|
||||||
isMappedTo("rightthumbstickright", input);
|
isMappedTo("rightthumbstickright", input);
|
||||||
}
|
}
|
||||||
else if (name == "up") {
|
else if (name == "up") {
|
||||||
return isMappedTo("up", input) || isMappedTo("leftthumbstickup", input) ||
|
return isMappedTo("up", input) || isMappedTo("leftthumbstickup", input) ||
|
||||||
isMappedTo("rightthumbstickup", input);
|
isMappedTo("rightthumbstickup", input);
|
||||||
}
|
}
|
||||||
else if (name == "down") {
|
else if (name == "down") {
|
||||||
return isMappedTo("down", input) || isMappedTo("leftthumbstickdown", input) ||
|
return isMappedTo("down", input) || isMappedTo("leftthumbstickdown", input) ||
|
||||||
isMappedTo("rightthumbstickdown", input);
|
isMappedTo("rightthumbstickdown", input);
|
||||||
}
|
}
|
||||||
else if (name == "leftshoulder") {
|
else if (name == "leftshoulder") {
|
||||||
return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input);
|
return isMappedTo("leftshoulder", input) || isMappedTo("pageup", input);
|
||||||
|
@ -182,8 +169,8 @@ void InputConfig::loadFromXML(pugi::xml_node& node)
|
||||||
InputType typeEnum = stringToInputType(type);
|
InputType typeEnum = stringToInputType(type);
|
||||||
|
|
||||||
if (typeEnum == TYPE_COUNT) {
|
if (typeEnum == TYPE_COUNT) {
|
||||||
LOG(LogError) << "InputConfig load error - input of type \"" << type <<
|
LOG(LogError) << "InputConfig load error - input of type \"" << type
|
||||||
"\" is invalid! Skipping input \"" << name << "\".\n";
|
<< "\" is invalid! Skipping input \"" << name << "\".\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,8 +178,7 @@ void InputConfig::loadFromXML(pugi::xml_node& node)
|
||||||
int value = input.attribute("value").as_int();
|
int value = input.attribute("value").as_int();
|
||||||
|
|
||||||
if (value == 0) {
|
if (value == 0) {
|
||||||
LOG(LogWarning) << "InputConfig value is 0 for " <<
|
LOG(LogWarning) << "InputConfig value is 0 for " << type << " " << id << "!\n";
|
||||||
type << " " << id << "!\n";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true);
|
mNameMap[toLower(name)] = Input(mDeviceId, typeEnum, id, value, true);
|
||||||
|
|
|
@ -9,18 +9,18 @@
|
||||||
#ifndef ES_CORE_INPUT_CONFIG_H
|
#ifndef ES_CORE_INPUT_CONFIG_H
|
||||||
#define ES_CORE_INPUT_CONFIG_H
|
#define ES_CORE_INPUT_CONFIG_H
|
||||||
|
|
||||||
|
#include <CECInput.h>
|
||||||
#include <SDL2/SDL_joystick.h>
|
#include <SDL2/SDL_joystick.h>
|
||||||
#include <SDL2/SDL_keyboard.h>
|
#include <SDL2/SDL_keyboard.h>
|
||||||
#include <CECInput.h>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#define DEVICE_KEYBOARD -1
|
#define DEVICE_KEYBOARD -1
|
||||||
#define DEVICE_CEC -2
|
#define DEVICE_CEC -2
|
||||||
|
|
||||||
enum InputType {
|
enum InputType {
|
||||||
TYPE_AXIS,
|
TYPE_AXIS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
TYPE_BUTTON,
|
TYPE_BUTTON,
|
||||||
TYPE_KEY,
|
TYPE_KEY,
|
||||||
TYPE_CEC_BUTTON,
|
TYPE_CEC_BUTTON,
|
||||||
|
@ -32,8 +32,7 @@ namespace pugi
|
||||||
class xml_node;
|
class xml_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Input
|
struct Input {
|
||||||
{
|
|
||||||
public:
|
public:
|
||||||
int device;
|
int device;
|
||||||
InputType type;
|
InputType type;
|
||||||
|
@ -50,23 +49,16 @@ public:
|
||||||
type = TYPE_COUNT;
|
type = TYPE_COUNT;
|
||||||
}
|
}
|
||||||
|
|
||||||
Input(
|
Input(int dev, InputType t, int i, int val, bool conf)
|
||||||
int dev,
|
: device(dev)
|
||||||
InputType t,
|
, type(t)
|
||||||
int i,
|
, id(i)
|
||||||
int val,
|
, value(val)
|
||||||
bool conf)
|
, configured(conf)
|
||||||
: device(dev),
|
|
||||||
type(t),id(i),
|
|
||||||
value(val),
|
|
||||||
configured(conf)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getCECButtonName(int keycode)
|
std::string getCECButtonName(int keycode) { return CECInput::getKeyCodeString(keycode); }
|
||||||
{
|
|
||||||
return CECInput::getKeyCodeString(keycode);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string string()
|
std::string string()
|
||||||
{
|
{
|
||||||
|
@ -113,8 +105,8 @@ public:
|
||||||
InputType stringToInputType(const std::string& type);
|
InputType stringToInputType(const std::string& type);
|
||||||
std::string toLower(std::string str);
|
std::string toLower(std::string str);
|
||||||
|
|
||||||
void clear();
|
void clear() { mNameMap.clear(); }
|
||||||
bool isConfigured();
|
bool isConfigured() { return mNameMap.size() > 0; }
|
||||||
|
|
||||||
void mapInput(const std::string& name, Input input);
|
void mapInput(const std::string& name, Input input);
|
||||||
void unmapInput(const std::string& name); // Unmap all Inputs mapped to this name.
|
void unmapInput(const std::string& name); // Unmap all Inputs mapped to this name.
|
||||||
|
@ -134,9 +126,9 @@ public:
|
||||||
void loadFromXML(pugi::xml_node& root);
|
void loadFromXML(pugi::xml_node& root);
|
||||||
void writeToXML(pugi::xml_node& parent);
|
void writeToXML(pugi::xml_node& parent);
|
||||||
|
|
||||||
inline int getDeviceId() const { return mDeviceId; };
|
int getDeviceId() const { return mDeviceId; }
|
||||||
inline const std::string& getDeviceName() { return mDeviceName; }
|
const std::string& getDeviceName() { return mDeviceName; }
|
||||||
inline const std::string& getDeviceGUIDString() { return mDeviceGUID; }
|
const std::string& getDeviceGUIDString() { return mDeviceGUID; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<std::string, Input> mNameMap;
|
std::map<std::string, Input> mNameMap;
|
||||||
|
|
|
@ -10,14 +10,14 @@
|
||||||
|
|
||||||
#include "InputManager.h"
|
#include "InputManager.h"
|
||||||
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "utils/FileSystemUtil.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "CECInput.h"
|
#include "CECInput.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Scripting.h"
|
#include "Scripting.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "resources/ResourceManager.h"
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
@ -30,12 +30,14 @@ int SDL_USER_CECBUTTONUP = -1;
|
||||||
|
|
||||||
InputManager* InputManager::sInstance = nullptr;
|
InputManager* InputManager::sInstance = nullptr;
|
||||||
|
|
||||||
InputManager::InputManager() : mKeyboardInputConfig(nullptr)
|
InputManager::InputManager()
|
||||||
|
: mKeyboardInputConfig(nullptr)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
InputManager::~InputManager()
|
InputManager::~InputManager()
|
||||||
{
|
{
|
||||||
|
// Deinit when destroyed.
|
||||||
deinit();
|
deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,8 +68,8 @@ void InputManager::init()
|
||||||
mConfigFileExists = true;
|
mConfigFileExists = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
mKeyboardInputConfig = std::make_unique<InputConfig>(DEVICE_KEYBOARD,
|
mKeyboardInputConfig =
|
||||||
"Keyboard", KEYBOARD_GUID_STRING);
|
std::make_unique<InputConfig>(DEVICE_KEYBOARD, "Keyboard", KEYBOARD_GUID_STRING);
|
||||||
|
|
||||||
bool customConfig = loadInputConfig(mKeyboardInputConfig.get());
|
bool customConfig = loadInputConfig(mKeyboardInputConfig.get());
|
||||||
|
|
||||||
|
@ -84,18 +86,18 @@ void InputManager::init()
|
||||||
// the bundled mapping is incorrect, or the SDL version is a bit older, it makes sense to be
|
// the bundled mapping is incorrect, or the SDL version is a bit older, it makes sense to be
|
||||||
// able to customize this. If a controller GUID is present in the mappings file that is
|
// able to customize this. If a controller GUID is present in the mappings file that is
|
||||||
// already present inside SDL, the custom mapping will overwrite the bundled one.
|
// already present inside SDL, the custom mapping will overwrite the bundled one.
|
||||||
std::string mappingsFile = Utils::FileSystem::getHomePath() +
|
std::string mappingsFile =
|
||||||
"/.emulationstation/" + "es_controller_mappings.cfg";
|
Utils::FileSystem::getHomePath() + "/.emulationstation/" + "es_controller_mappings.cfg";
|
||||||
|
|
||||||
if (!Utils::FileSystem::exists(mappingsFile))
|
if (!Utils::FileSystem::exists(mappingsFile))
|
||||||
mappingsFile = ResourceManager::getInstance()->
|
mappingsFile = ResourceManager::getInstance()->getResourcePath(
|
||||||
getResourcePath(":/controllers/es_controller_mappings.cfg");
|
":/controllers/es_controller_mappings.cfg");
|
||||||
|
|
||||||
int controllerMappings = SDL_GameControllerAddMappingsFromFile(mappingsFile.c_str());
|
int controllerMappings = SDL_GameControllerAddMappingsFromFile(mappingsFile.c_str());
|
||||||
|
|
||||||
if (controllerMappings != -1 && controllerMappings != 0) {
|
if (controllerMappings != -1 && controllerMappings != 0) {
|
||||||
LOG(LogInfo) << "Loaded " << controllerMappings << " controller " <<
|
LOG(LogInfo) << "Loaded " << controllerMappings << " controller "
|
||||||
(controllerMappings == 1 ? "mapping" : "mappings");
|
<< (controllerMappings == 1 ? "mapping" : "mappings");
|
||||||
}
|
}
|
||||||
|
|
||||||
int numJoysticks = SDL_NumJoysticks();
|
int numJoysticks = SDL_NumJoysticks();
|
||||||
|
@ -150,18 +152,20 @@ void InputManager::writeDeviceConfig(InputConfig* config)
|
||||||
std::string path = getConfigPath();
|
std::string path = getConfigPath();
|
||||||
|
|
||||||
LOG(LogDebug) << "InputManager::writeDeviceConfig(): "
|
LOG(LogDebug) << "InputManager::writeDeviceConfig(): "
|
||||||
"Saving input configuration file to \"" << path << "\"";
|
"Saving input configuration file to \""
|
||||||
|
<< path << "\"";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(path)) {
|
if (Utils::FileSystem::exists(path)) {
|
||||||
// Merge files.
|
// Merge files.
|
||||||
#if defined(_WIN64)
|
|
||||||
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result =
|
||||||
doc.load_file(Utils::String::stringToWideString(path).c_str());
|
doc.load_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Couldn't parse input configuration file: " << result.description();
|
LOG(LogError) << "Couldn't parse input configuration file: " << result.description();
|
||||||
}
|
}
|
||||||
|
@ -172,8 +176,8 @@ void InputManager::writeDeviceConfig(InputConfig* config)
|
||||||
// If inputAction @type=onfinish is set, let doOnFinish command take care of
|
// If inputAction @type=onfinish is set, let doOnFinish command take care of
|
||||||
// creating input configuration. We just put the input configuration into a
|
// creating input configuration. We just put the input configuration into a
|
||||||
// temporary input config file.
|
// temporary input config file.
|
||||||
pugi::xml_node actionnode = root.find_child_by_attribute("inputAction",
|
pugi::xml_node actionnode =
|
||||||
"type", "onfinish");
|
root.find_child_by_attribute("inputAction", "type", "onfinish");
|
||||||
if (actionnode) {
|
if (actionnode) {
|
||||||
path = getTemporaryConfigPath();
|
path = getTemporaryConfigPath();
|
||||||
doc.reset();
|
doc.reset();
|
||||||
|
@ -181,12 +185,12 @@ void InputManager::writeDeviceConfig(InputConfig* config)
|
||||||
root.append_copy(actionnode);
|
root.append_copy(actionnode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pugi::xml_node oldEntry = root.find_child_by_attribute("inputConfig",
|
pugi::xml_node oldEntry = root.find_child_by_attribute(
|
||||||
"deviceGUID", config->getDeviceGUIDString().c_str());
|
"inputConfig", "deviceGUID", config->getDeviceGUIDString().c_str());
|
||||||
if (oldEntry)
|
if (oldEntry)
|
||||||
root.remove_child(oldEntry);
|
root.remove_child(oldEntry);
|
||||||
oldEntry = root.find_child_by_attribute("inputConfig", "deviceName",
|
oldEntry = root.find_child_by_attribute("inputConfig", "deviceName",
|
||||||
config->getDeviceName().c_str());
|
config->getDeviceName().c_str());
|
||||||
if (oldEntry)
|
if (oldEntry)
|
||||||
root.remove_child(oldEntry);
|
root.remove_child(oldEntry);
|
||||||
}
|
}
|
||||||
|
@ -200,11 +204,11 @@ void InputManager::writeDeviceConfig(InputConfig* config)
|
||||||
|
|
||||||
config->writeToXML(root);
|
config->writeToXML(root);
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
doc.save_file(Utils::String::stringToWideString(path).c_str());
|
doc.save_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
doc.save_file(path.c_str());
|
doc.save_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Scripting::fireEvent("config-changed");
|
Scripting::fireEvent("config-changed");
|
||||||
Scripting::fireEvent("controls-changed");
|
Scripting::fireEvent("controls-changed");
|
||||||
|
@ -222,12 +226,12 @@ void InputManager::doOnFinish()
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(path)) {
|
if (Utils::FileSystem::exists(path)) {
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result =
|
||||||
doc.load_file(Utils::String::stringToWideString(path).c_str());
|
doc.load_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
pugi::xml_parse_result result = doc.load_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Couldn't parse input configuration file: " << result.description();
|
LOG(LogError) << "Couldn't parse input configuration file: " << result.description();
|
||||||
|
@ -238,18 +242,18 @@ void InputManager::doOnFinish()
|
||||||
root = root.find_child_by_attribute("inputAction", "type", "onfinish");
|
root = root.find_child_by_attribute("inputAction", "type", "onfinish");
|
||||||
if (root) {
|
if (root) {
|
||||||
for (pugi::xml_node command = root.child("command"); command;
|
for (pugi::xml_node command = root.child("command"); command;
|
||||||
command = command.next_sibling("command")) {
|
command = command.next_sibling("command")) {
|
||||||
std::string tocall = command.text().get();
|
std::string tocall = command.text().get();
|
||||||
|
|
||||||
LOG(LogInfo) << " " << tocall;
|
LOG(LogInfo) << " " << tocall;
|
||||||
std::cout << "==============================================\n"
|
std::cout << "==============================================\n"
|
||||||
"input config finish command:\n";
|
"input config finish command:\n";
|
||||||
int exitCode = runSystemCommand(tocall);
|
int exitCode = runSystemCommand(tocall);
|
||||||
std::cout << "==============================================\n";
|
std::cout << "==============================================\n";
|
||||||
|
|
||||||
if (exitCode != 0) {
|
if (exitCode != 0) {
|
||||||
LOG(LogWarning) << "...launch terminated with nonzero exit code " <<
|
LOG(LogWarning) << "...launch terminated with nonzero exit code "
|
||||||
exitCode << "!";
|
<< exitCode << "!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,11 +302,11 @@ int InputManager::getButtonCountByDevice(SDL_JoystickID id)
|
||||||
if (id == DEVICE_KEYBOARD)
|
if (id == DEVICE_KEYBOARD)
|
||||||
return -1;
|
return -1;
|
||||||
else if (id == DEVICE_CEC)
|
else if (id == DEVICE_CEC)
|
||||||
#if defined(HAVE_CECLIB)
|
#if defined(HAVE_CECLIB)
|
||||||
return CEC::CEC_USER_CONTROL_CODE_MAX;
|
return CEC::CEC_USER_CONTROL_CODE_MAX;
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
else
|
else
|
||||||
return SDL_JoystickNumButtons(mJoysticks[id]);
|
return SDL_JoystickNumButtons(mJoysticks[id]);
|
||||||
}
|
}
|
||||||
|
@ -343,10 +347,10 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
|
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case SDL_CONTROLLERAXISMOTION: {
|
case SDL_CONTROLLERAXISMOTION: {
|
||||||
// Whether to only accept input from the first controller.
|
// Whether to only accept input from the first controller.
|
||||||
if (Settings::getInstance()->getBool("InputOnlyFirstController"))
|
if (Settings::getInstance()->getBool("InputOnlyFirstController"))
|
||||||
if (mInputConfigs.begin()->first != event.cdevice.which)
|
if (mInputConfigs.begin()->first != event.cdevice.which)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// This is needed for a situation which sometimes occur when a game is launched,
|
// This is needed for a situation which sometimes occur when a game is launched,
|
||||||
// some axis input is generated and then the controller is disconnected before
|
// some axis input is generated and then the controller is disconnected before
|
||||||
|
@ -363,14 +367,17 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
int deadzone = 0;
|
int deadzone = 0;
|
||||||
|
|
||||||
if (event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
|
if (event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT ||
|
||||||
event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT)
|
event.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERRIGHT) {
|
||||||
deadzone = DEADZONE_TRIGGERS;
|
deadzone = DEADZONE_TRIGGERS;
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
deadzone = DEADZONE_THUMBSTICKS;
|
deadzone = DEADZONE_THUMBSTICKS;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the input value switched boundaries.
|
// Check if the input value switched boundaries.
|
||||||
if ((abs(axisValue) > deadzone) != (abs(mPrevAxisValues[
|
if ((abs(axisValue) > deadzone) !=
|
||||||
std::make_pair(event.caxis.which, event.caxis.axis)]) > deadzone)) {
|
(abs(mPrevAxisValues[std::make_pair(event.caxis.which, event.caxis.axis)]) >
|
||||||
|
deadzone)) {
|
||||||
int normValue;
|
int normValue;
|
||||||
if (abs(axisValue) <= deadzone) {
|
if (abs(axisValue) <= deadzone) {
|
||||||
normValue = 0;
|
normValue = 0;
|
||||||
|
@ -382,8 +389,9 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
normValue = -1;
|
normValue = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
window->input(getInputConfigByDevice(event.caxis.which), Input(event.caxis.which,
|
window->input(
|
||||||
TYPE_AXIS, event.caxis.axis, normValue, false));
|
getInputConfigByDevice(event.caxis.which),
|
||||||
|
Input(event.caxis.which, TYPE_AXIS, event.caxis.axis, normValue, false));
|
||||||
causedEvent = true;
|
causedEvent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -393,26 +401,27 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
case SDL_CONTROLLERBUTTONDOWN: {
|
case SDL_CONTROLLERBUTTONDOWN: {
|
||||||
}
|
}
|
||||||
case SDL_CONTROLLERBUTTONUP: {
|
case SDL_CONTROLLERBUTTONUP: {
|
||||||
// Whether to only accept input from the first controller.
|
// Whether to only accept input from the first controller.
|
||||||
if (Settings::getInstance()->getBool("InputOnlyFirstController"))
|
if (Settings::getInstance()->getBool("InputOnlyFirstController"))
|
||||||
if (mInputConfigs.begin()->first != event.cdevice.which)
|
if (mInputConfigs.begin()->first != event.cdevice.which)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// The event filtering below is required as some controllers send button presses
|
// The event filtering below is required as some controllers send button presses
|
||||||
// starting with the state 0 when using the D-pad. I consider this invalid behaviour
|
// starting with the state 0 when using the D-pad. I consider this invalid behaviour
|
||||||
// and the more popular controllers such as those from Microsoft and Sony do not show
|
// and the more popular controllers such as those from Microsoft and Sony do not show
|
||||||
// this strange behavior.
|
// this strange behavior.
|
||||||
int buttonState = mPrevButtonValues[
|
int buttonState =
|
||||||
std::make_pair(event.cbutton.which, event.cbutton.button)];
|
mPrevButtonValues[std::make_pair(event.cbutton.which, event.cbutton.button)];
|
||||||
|
|
||||||
if ((buttonState == -1 || buttonState == 0) && event.cbutton.state == 0)
|
if ((buttonState == -1 || buttonState == 0) && event.cbutton.state == 0)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
mPrevButtonValues[std::make_pair(event.cbutton.which, event.cbutton.button)] =
|
mPrevButtonValues[std::make_pair(event.cbutton.which, event.cbutton.button)] =
|
||||||
event.cbutton.state;
|
event.cbutton.state;
|
||||||
|
|
||||||
window->input(getInputConfigByDevice(event.cbutton.which), Input(event.cbutton.which,
|
window->input(getInputConfigByDevice(event.cbutton.which),
|
||||||
TYPE_BUTTON, event.cbutton.button, event.cbutton.state == SDL_PRESSED, false));
|
Input(event.cbutton.which, TYPE_BUTTON, event.cbutton.button,
|
||||||
|
event.cbutton.state == SDL_PRESSED, false));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -430,13 +439,13 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
window->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD,
|
window->input(getInputConfigByDevice(DEVICE_KEYBOARD),
|
||||||
TYPE_KEY, event.key.keysym.sym, 1, false));
|
Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 1, false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_KEYUP: {
|
case SDL_KEYUP: {
|
||||||
window->input(getInputConfigByDevice(DEVICE_KEYBOARD), Input(DEVICE_KEYBOARD,
|
window->input(getInputConfigByDevice(DEVICE_KEYBOARD),
|
||||||
TYPE_KEY, event.key.keysym.sym, 0, false));
|
Input(DEVICE_KEYBOARD, TYPE_KEY, event.key.keysym.sym, 0, false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
case SDL_TEXTINPUT: {
|
case SDL_TEXTINPUT: {
|
||||||
|
@ -454,21 +463,17 @@ bool InputManager::parseEvent(const SDL_Event& event, Window* window)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.type == static_cast<unsigned int>(SDL_USER_CECBUTTONDOWN)) ||
|
if ((event.type == static_cast<unsigned int>(SDL_USER_CECBUTTONDOWN)) ||
|
||||||
(event.type == static_cast<unsigned int>(SDL_USER_CECBUTTONUP))) {
|
(event.type == static_cast<unsigned int>(SDL_USER_CECBUTTONUP))) {
|
||||||
window->input(getInputConfigByDevice(DEVICE_CEC), Input(DEVICE_CEC,
|
window->input(getInputConfigByDevice(DEVICE_CEC),
|
||||||
TYPE_CEC_BUTTON, event.user.code, event.type ==
|
Input(DEVICE_CEC, TYPE_CEC_BUTTON, event.user.code,
|
||||||
static_cast<unsigned int>(SDL_USER_CECBUTTONDOWN), false));
|
event.type == static_cast<unsigned int>(SDL_USER_CECBUTTONDOWN),
|
||||||
|
false));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool InputManager::initialized() const
|
|
||||||
{
|
|
||||||
return mKeyboardInputConfig != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool InputManager::loadInputConfig(InputConfig* config)
|
bool InputManager::loadInputConfig(InputConfig* config)
|
||||||
{
|
{
|
||||||
if (!mConfigFileExists)
|
if (!mConfigFileExists)
|
||||||
|
@ -477,11 +482,11 @@ bool InputManager::loadInputConfig(InputConfig* config)
|
||||||
std::string path = getConfigPath();
|
std::string path = getConfigPath();
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
|
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!res) {
|
if (!res) {
|
||||||
LOG(LogError) << "Couldn't parse the input configuration file: " << res.description();
|
LOG(LogError) << "Couldn't parse the input configuration file: " << res.description();
|
||||||
|
@ -492,16 +497,16 @@ bool InputManager::loadInputConfig(InputConfig* config)
|
||||||
if (!root)
|
if (!root)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
pugi::xml_node configNode = root.find_child_by_attribute("inputConfig",
|
pugi::xml_node configNode = root.find_child_by_attribute("inputConfig", "deviceGUID",
|
||||||
"deviceGUID", config->getDeviceGUIDString().c_str());
|
config->getDeviceGUIDString().c_str());
|
||||||
|
|
||||||
// Enabling this will match an entry in es_input.xml based on the device name if there
|
// Enabling this will match an entry in es_input.xml based on the device name if there
|
||||||
// was no GUID match. This is probably not a good idea as many controllers share the same
|
// was no GUID match. This is probably not a good idea as many controllers share the same
|
||||||
// name even though the GUID differ and potentially the button configuration could be
|
// name even though the GUID differ and potentially the button configuration could be
|
||||||
// different between them. Keeping the code for now though.
|
// different between them. Keeping the code for now though.
|
||||||
// if (!configNode)
|
// if (!configNode)
|
||||||
// configNode = root.find_child_by_attribute("inputConfig",
|
// configNode = root.find_child_by_attribute("inputConfig",
|
||||||
// "deviceName", config->getDeviceName().c_str());
|
// "deviceName", config->getDeviceName().c_str());
|
||||||
|
|
||||||
// With the move to the SDL GameController API the button layout changed quite a lot, so
|
// With the move to the SDL GameController API the button layout changed quite a lot, so
|
||||||
// es_input.xml files generated using the old API will end up with a completely unusable
|
// es_input.xml files generated using the old API will end up with a completely unusable
|
||||||
|
@ -535,11 +540,11 @@ void InputManager::loadDefaultKBConfig()
|
||||||
cfg->mapInput("A", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_RETURN, 1, true));
|
cfg->mapInput("A", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_RETURN, 1, true));
|
||||||
cfg->mapInput("B", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_BACKSPACE, 1, true));
|
cfg->mapInput("B", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_BACKSPACE, 1, true));
|
||||||
cfg->mapInput("X", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_DELETE, 1, true));
|
cfg->mapInput("X", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_DELETE, 1, true));
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PRINTSCREEN, 1, true));
|
cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_PRINTSCREEN, 1, true));
|
||||||
#else
|
#else
|
||||||
cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_INSERT, 1, true));
|
cfg->mapInput("Y", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_INSERT, 1, true));
|
||||||
#endif
|
#endif
|
||||||
cfg->mapInput("Start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_ESCAPE, 1, true));
|
cfg->mapInput("Start", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_ESCAPE, 1, true));
|
||||||
cfg->mapInput("Back", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
cfg->mapInput("Back", Input(DEVICE_KEYBOARD, TYPE_KEY, SDLK_F1, 1, true));
|
||||||
|
|
||||||
|
@ -559,6 +564,7 @@ void InputManager::loadDefaultControllerConfig(SDL_JoystickID deviceIndex)
|
||||||
if (cfg->isConfigured())
|
if (cfg->isConfigured())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
cfg->mapInput("Up", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_UP, 1, true));
|
cfg->mapInput("Up", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_UP, 1, true));
|
||||||
cfg->mapInput("Down", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_DOWN, 1, true));
|
cfg->mapInput("Down", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_DOWN, 1, true));
|
||||||
cfg->mapInput("Left", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 1, true));
|
cfg->mapInput("Left", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_DPAD_LEFT, 1, true));
|
||||||
|
@ -583,6 +589,7 @@ void InputManager::loadDefaultControllerConfig(SDL_JoystickID deviceIndex)
|
||||||
cfg->mapInput("RightThumbstickLeft", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, -1, true));
|
cfg->mapInput("RightThumbstickLeft", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, -1, true));
|
||||||
cfg->mapInput("RightThumbstickRight", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, 1, true));
|
cfg->mapInput("RightThumbstickRight", Input(deviceIndex, TYPE_AXIS, SDL_CONTROLLER_AXIS_RIGHTX, 1, true));
|
||||||
cfg->mapInput("RightThumbstickClick", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_RIGHTSTICK, 1, true));
|
cfg->mapInput("RightThumbstickClick", Input(deviceIndex, TYPE_BUTTON, SDL_CONTROLLER_BUTTON_RIGHTSTICK, 1, true));
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputManager::addControllerByDeviceIndex(int deviceIndex)
|
void InputManager::addControllerByDeviceIndex(int deviceIndex)
|
||||||
|
@ -599,21 +606,21 @@ void InputManager::addControllerByDeviceIndex(int deviceIndex)
|
||||||
char guid[65];
|
char guid[65];
|
||||||
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
|
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
|
||||||
|
|
||||||
mInputConfigs[joyID] = std::make_unique<InputConfig>(
|
mInputConfigs[joyID] =
|
||||||
joyID, SDL_GameControllerName(mControllers[joyID]), guid);
|
std::make_unique<InputConfig>(joyID, SDL_GameControllerName(mControllers[joyID]), guid);
|
||||||
|
|
||||||
bool customConfig = loadInputConfig(mInputConfigs[joyID].get());
|
bool customConfig = loadInputConfig(mInputConfigs[joyID].get());
|
||||||
|
|
||||||
if (customConfig) {
|
if (customConfig) {
|
||||||
LOG(LogInfo) << "Added controller with custom configuration: \"" <<
|
LOG(LogInfo) << "Added controller with custom configuration: \""
|
||||||
SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid <<
|
<< SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid
|
||||||
", instance ID: " << joyID << ", device index: " << deviceIndex << ")";
|
<< ", instance ID: " << joyID << ", device index: " << deviceIndex << ")";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
loadDefaultControllerConfig(joyID);
|
loadDefaultControllerConfig(joyID);
|
||||||
LOG(LogInfo) << "Added controller with default configuration: \"" <<
|
LOG(LogInfo) << "Added controller with default configuration: \""
|
||||||
SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid <<
|
<< SDL_GameControllerName(mControllers[joyID]) << "\" (GUID: " << guid
|
||||||
", instance ID: " << joyID << ", device index: " << deviceIndex << ")";
|
<< ", instance ID: " << joyID << ", device index: " << deviceIndex << ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
int numAxes = SDL_JoystickNumAxes(joy);
|
int numAxes = SDL_JoystickNumAxes(joy);
|
||||||
|
@ -634,8 +641,8 @@ void InputManager::removeControllerByJoystickID(SDL_JoystickID joyID)
|
||||||
SDL_Joystick* joy = SDL_JoystickFromInstanceID(joyID);
|
SDL_Joystick* joy = SDL_JoystickFromInstanceID(joyID);
|
||||||
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
|
SDL_JoystickGetGUIDString(SDL_JoystickGetGUID(joy), guid, 65);
|
||||||
|
|
||||||
LOG(LogInfo) << "Removed controller \"" << SDL_GameControllerName(mControllers[joyID]) <<
|
LOG(LogInfo) << "Removed controller \"" << SDL_GameControllerName(mControllers[joyID])
|
||||||
"\" (GUID: " << guid << ", instance ID: " << joyID << ")";
|
<< "\" (GUID: " << guid << ", instance ID: " << joyID << ")";
|
||||||
|
|
||||||
// Delete mPrevAxisValues for the device.
|
// Delete mPrevAxisValues for the device.
|
||||||
int axisEntries = static_cast<int>(mPrevAxisValues.size());
|
int axisEntries = static_cast<int>(mPrevAxisValues.size());
|
||||||
|
|
|
@ -55,7 +55,7 @@ public:
|
||||||
int getNumJoysticks() { return static_cast<int>(mJoysticks.size()); }
|
int getNumJoysticks() { return static_cast<int>(mJoysticks.size()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool initialized() const;
|
bool initialized() const { return mKeyboardInputConfig != nullptr; }
|
||||||
|
|
||||||
bool loadInputConfig(InputConfig* config);
|
bool loadInputConfig(InputConfig* config);
|
||||||
void loadDefaultKBConfig();
|
void loadDefaultKBConfig();
|
||||||
|
|
|
@ -8,32 +8,16 @@
|
||||||
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
#include "utils/FileSystemUtil.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <iostream>
|
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
LogLevel Log::reportingLevel = LogInfo;
|
LogLevel Log::reportingLevel = LogInfo;
|
||||||
std::ofstream file;
|
std::ofstream file;
|
||||||
|
|
||||||
LogLevel Log::getReportingLevel()
|
|
||||||
{
|
|
||||||
return reportingLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Log::getLogPath()
|
|
||||||
{
|
|
||||||
return Utils::FileSystem::getHomePath() + "/.emulationstation/es_log.txt";
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log::setReportingLevel(LogLevel level)
|
|
||||||
{
|
|
||||||
reportingLevel = level;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Log::init()
|
void Log::init()
|
||||||
{
|
{
|
||||||
Utils::FileSystem::removeFile(getLogPath() + ".bak");
|
Utils::FileSystem::removeFile(getLogPath() + ".bak");
|
||||||
|
@ -44,24 +28,24 @@ void Log::init()
|
||||||
|
|
||||||
void Log::open()
|
void Log::open()
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
file.open(Utils::String::stringToWideString(getLogPath()).c_str());
|
file.open(Utils::String::stringToWideString(getLogPath()).c_str());
|
||||||
#else
|
#else
|
||||||
file.open(getLogPath().c_str());
|
file.open(getLogPath().c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::ostringstream& Log::get(LogLevel level)
|
std::ostringstream& Log::get(LogLevel level)
|
||||||
{
|
{
|
||||||
time_t t = time(nullptr);
|
time_t t = time(nullptr);
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// Of course Windows does not follow standards and puts the parameters the other way
|
// Of course Windows does not follow standards and puts the parameters the other way
|
||||||
// around compared to POSIX.
|
// around compared to POSIX.
|
||||||
localtime_s(&tm, &t);
|
localtime_s(&tm, &t);
|
||||||
#else
|
#else
|
||||||
localtime_r(&t, &tm);
|
localtime_r(&t, &tm);
|
||||||
#endif
|
#endif
|
||||||
os << std::put_time(&tm, "%b %d %T ") << logLevelMap[level] << ":\t";
|
os << std::put_time(&tm, "%b %d %T ") << logLevelMap[level] << ":\t";
|
||||||
messageLevel = level;
|
messageLevel = level;
|
||||||
|
|
||||||
|
@ -70,6 +54,7 @@ std::ostringstream& Log::get(LogLevel level)
|
||||||
|
|
||||||
void Log::flush()
|
void Log::flush()
|
||||||
{
|
{
|
||||||
|
// This runs on application exit.
|
||||||
file.flush();
|
file.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,8 +70,8 @@ Log::~Log()
|
||||||
|
|
||||||
if (!file.is_open()) {
|
if (!file.is_open()) {
|
||||||
// Not open yet, print to stdout.
|
// Not open yet, print to stdout.
|
||||||
std::cerr << "ERROR - tried to write to log file before it was open! "
|
std::cerr << "Error: Tried to write to log file before it was open, "
|
||||||
"The following won't be logged:\n";
|
"the following won't be logged:\n";
|
||||||
std::cerr << os.str();
|
std::cerr << os.str();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,19 @@
|
||||||
#ifndef ES_CORE_LOG_H
|
#ifndef ES_CORE_LOG_H
|
||||||
#define ES_CORE_LOG_H
|
#define ES_CORE_LOG_H
|
||||||
|
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
|
||||||
#define LOG(level) \
|
#define LOG(level) \
|
||||||
if (level > Log::getReportingLevel()); \
|
if (level > Log::getReportingLevel()) \
|
||||||
else Log().get(level)
|
; \
|
||||||
|
else \
|
||||||
|
Log().get(level)
|
||||||
|
|
||||||
enum LogLevel {
|
enum LogLevel {
|
||||||
LogError,
|
LogError, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
LogWarning,
|
LogWarning,
|
||||||
LogInfo,
|
LogInfo,
|
||||||
LogDebug
|
LogDebug
|
||||||
|
@ -29,10 +33,12 @@ public:
|
||||||
~Log();
|
~Log();
|
||||||
std::ostringstream& get(LogLevel level = LogInfo);
|
std::ostringstream& get(LogLevel level = LogInfo);
|
||||||
|
|
||||||
static LogLevel getReportingLevel();
|
static LogLevel getReportingLevel() { return reportingLevel; }
|
||||||
static void setReportingLevel(LogLevel level);
|
static void setReportingLevel(LogLevel level) { reportingLevel = level; }
|
||||||
|
static std::string getLogPath()
|
||||||
static std::string getLogPath();
|
{
|
||||||
|
return Utils::FileSystem::getHomePath() + "/.emulationstation/es_log.txt";
|
||||||
|
}
|
||||||
|
|
||||||
static void flush();
|
static void flush();
|
||||||
static void init();
|
static void init();
|
||||||
|
@ -43,11 +49,11 @@ protected:
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::map<LogLevel, std::string> logLevelMap {
|
std::map<LogLevel, std::string> logLevelMap { // Log level indicators.
|
||||||
{ LogError, "Error" },
|
{ LogError, "Error" },
|
||||||
{ LogWarning, "Warn" },
|
{ LogWarning, "Warn" },
|
||||||
{ LogInfo, "Info" },
|
{ LogInfo, "Info" },
|
||||||
{ LogDebug, "Debug" }
|
{ LogDebug, "Debug" }
|
||||||
};
|
};
|
||||||
|
|
||||||
static LogLevel reportingLevel;
|
static LogLevel reportingLevel;
|
||||||
|
|
|
@ -11,10 +11,10 @@
|
||||||
|
|
||||||
#include "MameNames.h"
|
#include "MameNames.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -53,25 +53,23 @@ MameNames::MameNames()
|
||||||
LOG(LogInfo) << "Parsing MAME names file \"" << xmlpath << "\"...";
|
LOG(LogInfo) << "Parsing MAME names file \"" << xmlpath << "\"...";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result =
|
||||||
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
pugi::xml_parse_result result = doc.load_file(xmlpath.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Error parsing MAME names file \"" << xmlpath << "\": "
|
LOG(LogError) << "Error parsing MAME names file \"" << xmlpath
|
||||||
<< result.description();
|
<< "\": " << result.description();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node gameNode = doc.child("game");
|
for (pugi::xml_node gameNode = doc.child("game"); gameNode;
|
||||||
gameNode; gameNode = gameNode.next_sibling("game")) {
|
gameNode = gameNode.next_sibling("game")) {
|
||||||
NamePair namePair = {
|
NamePair namePair = { gameNode.child("mamename").text().get(),
|
||||||
gameNode.child("mamename").text().get(),
|
gameNode.child("realname").text().get() };
|
||||||
gameNode.child("realname").text().get()
|
|
||||||
};
|
|
||||||
mNamePairs.push_back(namePair);
|
mNamePairs.push_back(namePair);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,20 +81,20 @@ MameNames::MameNames()
|
||||||
|
|
||||||
LOG(LogInfo) << "Parsing MAME BIOSes file \"" << xmlpath << "\"...";
|
LOG(LogInfo) << "Parsing MAME BIOSes file \"" << xmlpath << "\"...";
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
||||||
#else
|
#else
|
||||||
result = doc.load_file(xmlpath.c_str());
|
result = doc.load_file(xmlpath.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Error parsing MAME BIOSes file \"" << xmlpath << "\": "
|
LOG(LogError) << "Error parsing MAME BIOSes file \"" << xmlpath
|
||||||
<< result.description();
|
<< "\": " << result.description();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node biosNode = doc.child("bios");
|
for (pugi::xml_node biosNode = doc.child("bios"); biosNode;
|
||||||
biosNode; biosNode = biosNode.next_sibling("bios")) {
|
biosNode = biosNode.next_sibling("bios")) {
|
||||||
std::string bios = biosNode.text().get();
|
std::string bios = biosNode.text().get();
|
||||||
mMameBioses.push_back(bios);
|
mMameBioses.push_back(bios);
|
||||||
}
|
}
|
||||||
|
@ -109,29 +107,25 @@ MameNames::MameNames()
|
||||||
|
|
||||||
LOG(LogInfo) << "Parsing MAME devices file \"" << xmlpath << "\"...";
|
LOG(LogInfo) << "Parsing MAME devices file \"" << xmlpath << "\"...";
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
result = doc.load_file(Utils::String::stringToWideString(xmlpath).c_str());
|
||||||
#else
|
#else
|
||||||
result = doc.load_file(xmlpath.c_str());
|
result = doc.load_file(xmlpath.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Error parsing MAME devices file \"" << xmlpath << "\": "
|
LOG(LogError) << "Error parsing MAME devices file \"" << xmlpath
|
||||||
<< result.description();
|
<< "\": " << result.description();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pugi::xml_node deviceNode = doc.child("device");
|
for (pugi::xml_node deviceNode = doc.child("device"); deviceNode;
|
||||||
deviceNode; deviceNode = deviceNode.next_sibling("device")) {
|
deviceNode = deviceNode.next_sibling("device")) {
|
||||||
std::string device = deviceNode.text().get();
|
std::string device = deviceNode.text().get();
|
||||||
mMameDevices.push_back(device);
|
mMameDevices.push_back(device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MameNames::~MameNames()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MameNames::getRealName(const std::string& _mameName)
|
std::string MameNames::getRealName(const std::string& _mameName)
|
||||||
{
|
{
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
|
@ -158,17 +152,6 @@ std::string MameNames::getCleanName(const std::string& _mameName)
|
||||||
return cleanName;
|
return cleanName;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MameNames::isBios(const std::string& _biosName)
|
|
||||||
{
|
|
||||||
return MameNames::find(mMameBioses, _biosName);
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool MameNames::isDevice(const std::string& _deviceName)
|
|
||||||
{
|
|
||||||
return MameNames::find(mMameDevices, _deviceName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool MameNames::find(std::vector<std::string> devices, const std::string& name)
|
const bool MameNames::find(std::vector<std::string> devices, const std::string& name)
|
||||||
{
|
{
|
||||||
size_t start = 0;
|
size_t start = 0;
|
||||||
|
|
|
@ -24,8 +24,14 @@ public:
|
||||||
static MameNames* getInstance();
|
static MameNames* getInstance();
|
||||||
std::string getRealName(const std::string& _mameName);
|
std::string getRealName(const std::string& _mameName);
|
||||||
std::string getCleanName(const std::string& _mameName);
|
std::string getCleanName(const std::string& _mameName);
|
||||||
const bool isBios(const std::string& _biosName);
|
const bool isBios(const std::string& _biosName)
|
||||||
const bool isDevice(const std::string& _deviceName);
|
{
|
||||||
|
return MameNames::find(mMameBioses, _biosName);
|
||||||
|
}
|
||||||
|
const bool isDevice(const std::string& _deviceName)
|
||||||
|
{
|
||||||
|
return MameNames::find(mMameDevices, _deviceName);
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct NamePair {
|
struct NamePair {
|
||||||
|
@ -35,8 +41,8 @@ private:
|
||||||
|
|
||||||
typedef std::vector<NamePair> namePairVector;
|
typedef std::vector<NamePair> namePairVector;
|
||||||
|
|
||||||
MameNames();
|
MameNames();
|
||||||
~MameNames();
|
~MameNames() {}
|
||||||
|
|
||||||
static MameNames* sInstance;
|
static MameNames* sInstance;
|
||||||
|
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "MameNames.h"
|
#include "MameNames.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_events.h>
|
#include <SDL2/SDL_events.h>
|
||||||
|
|
||||||
|
@ -52,36 +52,36 @@ int runPoweroffCommand()
|
||||||
|
|
||||||
int runSystemCommand(const std::string& cmd_utf8)
|
int runSystemCommand(const std::string& cmd_utf8)
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// On Windows we use _wsystem to support non-ASCII paths
|
// On Windows we use _wsystem to support non-ASCII paths
|
||||||
// which requires converting from UTF-8 to a wstring.
|
// which requires converting from UTF-8 to a wstring.
|
||||||
std::wstring wchar_str = Utils::String::stringToWideString(cmd_utf8);
|
std::wstring wchar_str = Utils::String::stringToWideString(cmd_utf8);
|
||||||
return _wsystem(wchar_str.c_str());
|
return _wsystem(wchar_str.c_str());
|
||||||
#else
|
#else
|
||||||
return system(cmd_utf8.c_str());
|
return system(cmd_utf8.c_str());
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int runSystemCommand(const std::wstring& cmd_utf16)
|
int runSystemCommand(const std::wstring& cmd_utf16)
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
return _wsystem(cmd_utf16.c_str());
|
return _wsystem(cmd_utf16.c_str());
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int launchGameUnix(const std::string& cmd_utf8, bool runInBackground)
|
int launchGameUnix(const std::string& cmd_utf8, bool runInBackground)
|
||||||
{
|
{
|
||||||
#if defined(__unix__) || defined (__APPLE__)
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
std::string command = std::string(cmd_utf8) + " 2>&1 &";
|
std::string command = std::string(cmd_utf8) + " 2>&1 &";
|
||||||
|
|
||||||
// Launching games while keeping ES-DE running in the background is very crude as for
|
// Launching games while keeping ES-DE running in the background is very crude as for
|
||||||
// instance no output from the command is captured and no real error handling is
|
// instance no output from the command is captured and no real error handling is
|
||||||
// implemented. It should therefore only be used when absolutely necessary.
|
// implemented. It should therefore only be used when absolutely necessary.
|
||||||
if (runInBackground) {
|
if (runInBackground) {
|
||||||
LOG(LogDebug) << "Platform::launchGameUnix(): Launching game while keeping ES-DE "
|
LOG(LogDebug) << "Platform::launchGameUnix(): Launching game while keeping ES-DE running "
|
||||||
"running in the background, no command output will be written to the log file";
|
"in the background, no command output will be written to the log file";
|
||||||
return system(command.c_str());
|
return system(command.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -106,12 +106,11 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground)
|
||||||
// Remove any trailing newline from the command output.
|
// Remove any trailing newline from the command output.
|
||||||
if (commandOutput.size()) {
|
if (commandOutput.size()) {
|
||||||
if (commandOutput.back() == '\n')
|
if (commandOutput.back() == '\n')
|
||||||
commandOutput.pop_back();
|
commandOutput.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (returnValue) {
|
if (returnValue) {
|
||||||
LOG(LogError) << "launchGameUnix - return value " <<
|
LOG(LogError) << "launchGameUnix - return value " << std::to_string(returnValue) + ":";
|
||||||
std::to_string(returnValue) + ":";
|
|
||||||
if (commandOutput.size())
|
if (commandOutput.size())
|
||||||
LOG(LogError) << commandOutput;
|
LOG(LogError) << commandOutput;
|
||||||
else
|
else
|
||||||
|
@ -124,14 +123,14 @@ int launchGameUnix(const std::string& cmd_utf8, bool runInBackground)
|
||||||
|
|
||||||
return returnValue;
|
return returnValue;
|
||||||
|
|
||||||
#else // __unix__
|
#else // __unix__
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
STARTUPINFOW si {};
|
STARTUPINFOW si {};
|
||||||
PROCESS_INFORMATION pi;
|
PROCESS_INFORMATION pi;
|
||||||
|
|
||||||
|
@ -139,17 +138,19 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
||||||
bool processReturnValue = true;
|
bool processReturnValue = true;
|
||||||
DWORD errorCode = 0;
|
DWORD errorCode = 0;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
processReturnValue = CreateProcessW(
|
processReturnValue = CreateProcessW(
|
||||||
nullptr, // No application name (use command line).
|
nullptr, // No application name (use command line).
|
||||||
const_cast<wchar_t*>(cmd_utf16.c_str()), // Command line.
|
const_cast<wchar_t*>(cmd_utf16.c_str()), // Command line.
|
||||||
nullptr, // Process attributes.
|
nullptr, // Process attributes.
|
||||||
nullptr, // Thread attributes.
|
nullptr, // Thread attributes.
|
||||||
FALSE, // Handles inheritance.
|
FALSE, // Handles inheritance.
|
||||||
0, // Creation flags.
|
0, // Creation flags.
|
||||||
nullptr, // Use parent's environment block.
|
nullptr, // Use parent's environment block.
|
||||||
nullptr, // Use parent's starting directory.
|
nullptr, // Use parent's starting directory.
|
||||||
&si, // Pointer to the STARTUPINFOW structure.
|
&si, // Pointer to the STARTUPINFOW structure.
|
||||||
&pi); // Pointer to the PROCESS_INFORMATION structure.
|
&pi); // Pointer to the PROCESS_INFORMATION structure.
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
if (!runInBackground) {
|
if (!runInBackground) {
|
||||||
if (Settings::getInstance()->getBool("LaunchWorkaround")) {
|
if (Settings::getInstance()->getBool("LaunchWorkaround")) {
|
||||||
|
@ -169,9 +170,9 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
||||||
if (!processReturnValue) {
|
if (!processReturnValue) {
|
||||||
LPWSTR pBuffer = nullptr;
|
LPWSTR pBuffer = nullptr;
|
||||||
|
|
||||||
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr,
|
||||||
nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
reinterpret_cast<LPWSTR>(&pBuffer), 0, nullptr);
|
reinterpret_cast<LPWSTR>(&pBuffer), 0, nullptr);
|
||||||
|
|
||||||
errorCode = GetLastError();
|
errorCode = GetLastError();
|
||||||
|
|
||||||
|
@ -186,8 +187,8 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(LogError) << "launchGameWindows - system error code " <<
|
LOG(LogError) << "launchGameWindows - system error code " << errorCode << ": "
|
||||||
errorCode << ": " << errorMessage;
|
<< errorMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close process and thread handles.
|
// Close process and thread handles.
|
||||||
|
@ -196,40 +197,40 @@ int launchGameWindows(const std::wstring& cmd_utf16, bool runInBackground)
|
||||||
|
|
||||||
return errorCode;
|
return errorCode;
|
||||||
|
|
||||||
#else // _WIN64
|
#else // _WIN64
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int getTaskbarState()
|
unsigned int getTaskbarState()
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
APPBARDATA barData;
|
APPBARDATA barData;
|
||||||
barData.cbSize = sizeof(APPBARDATA);
|
barData.cbSize = sizeof(APPBARDATA);
|
||||||
return static_cast<UINT>(SHAppBarMessage(ABM_GETSTATE, &barData));
|
return static_cast<UINT>(SHAppBarMessage(ABM_GETSTATE, &barData));
|
||||||
#else
|
#else
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void hideTaskbar()
|
void hideTaskbar()
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
APPBARDATA barData;
|
APPBARDATA barData;
|
||||||
barData.cbSize = sizeof(APPBARDATA);
|
barData.cbSize = sizeof(APPBARDATA);
|
||||||
barData.lParam = ABS_AUTOHIDE;
|
barData.lParam = ABS_AUTOHIDE;
|
||||||
SHAppBarMessage(ABM_SETSTATE, &barData);
|
SHAppBarMessage(ABM_SETSTATE, &barData);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void revertTaskbarState(unsigned int& state)
|
void revertTaskbarState(unsigned int& state)
|
||||||
{
|
{
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
APPBARDATA barData;
|
APPBARDATA barData;
|
||||||
barData.cbSize = sizeof(APPBARDATA);
|
barData.cbSize = sizeof(APPBARDATA);
|
||||||
barData.lParam = state;
|
barData.lParam = state;
|
||||||
SHAppBarMessage(ABM_SETSTATE, &barData);
|
SHAppBarMessage(ABM_SETSTATE, &barData);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
QuitMode quitMode = QuitMode::QUIT;
|
QuitMode quitMode = QuitMode::QUIT;
|
||||||
|
@ -264,7 +265,7 @@ void touch(const std::string& filename)
|
||||||
if (fp != nullptr)
|
if (fp != nullptr)
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
#else
|
#else
|
||||||
int fd = open(filename.c_str(), O_CREAT|O_WRONLY, 0644);
|
int fd = open(filename.c_str(), O_CREAT | O_WRONLY, 0644);
|
||||||
if (fd >= 0)
|
if (fd >= 0)
|
||||||
close(fd);
|
close(fd);
|
||||||
#endif
|
#endif
|
||||||
|
@ -273,15 +274,18 @@ void touch(const std::string& filename)
|
||||||
void processQuitMode()
|
void processQuitMode()
|
||||||
{
|
{
|
||||||
switch (quitMode) {
|
switch (quitMode) {
|
||||||
case QuitMode::REBOOT:
|
case QuitMode::REBOOT: {
|
||||||
LOG(LogInfo) << "Rebooting system";
|
LOG(LogInfo) << "Rebooting system";
|
||||||
runRebootCommand();
|
runRebootCommand();
|
||||||
break;
|
break;
|
||||||
case QuitMode::POWEROFF:
|
}
|
||||||
LOG(LogInfo) << "Powering off system";
|
case QuitMode::POWEROFF: {
|
||||||
runPoweroffCommand();
|
LOG(LogInfo) << "Powering off system";
|
||||||
break;
|
runPoweroffCommand();
|
||||||
default:
|
break;
|
||||||
break;
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,12 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
|
#include <winsock2.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum QuitMode {
|
enum QuitMode {
|
||||||
QUIT = 0,
|
QUIT = 0, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
REBOOT = 1,
|
REBOOT = 1,
|
||||||
POWEROFF = 2
|
POWEROFF = 2
|
||||||
};
|
};
|
||||||
|
@ -35,6 +36,7 @@ void revertTaskbarState(unsigned int& state);
|
||||||
|
|
||||||
// Clean, normal shutdown.
|
// Clean, normal shutdown.
|
||||||
int quitES(QuitMode mode = QuitMode::QUIT);
|
int quitES(QuitMode mode = QuitMode::QUIT);
|
||||||
|
|
||||||
// Immediately shut down the application as cleanly as possible.
|
// Immediately shut down the application as cleanly as possible.
|
||||||
void emergencyShutdown();
|
void emergencyShutdown();
|
||||||
void processQuitMode();
|
void processQuitMode();
|
||||||
|
|
|
@ -14,20 +14,20 @@
|
||||||
|
|
||||||
#include "Scripting.h"
|
#include "Scripting.h"
|
||||||
|
|
||||||
#include "utils/FileSystemUtil.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
|
||||||
namespace Scripting
|
namespace Scripting
|
||||||
{
|
{
|
||||||
void fireEvent(const std::string& eventName, const std::string& arg1, const std::string& arg2)
|
void fireEvent(const std::string& eventName, const std::string& arg1, const std::string& arg2)
|
||||||
{
|
{
|
||||||
if (!Settings::getInstance()->getBool("CustomEventScripts"))
|
if (!Settings::getInstance()->getBool("CustomEventScripts"))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
LOG(LogDebug) << "Scripting::fireEvent(): " << eventName << " \"" << arg1 <<
|
LOG(LogDebug) << "Scripting::fireEvent(): " << eventName << " \"" << arg1 << "\" \"" << arg2
|
||||||
"\" \"" << arg2 << "\"";
|
<< "\"";
|
||||||
|
|
||||||
std::list<std::string> scriptDirList;
|
std::list<std::string> scriptDirList;
|
||||||
std::string scriptDir;
|
std::string scriptDir;
|
||||||
|
@ -38,10 +38,10 @@ namespace Scripting
|
||||||
scriptDirList.push_back(scriptDir);
|
scriptDirList.push_back(scriptDir);
|
||||||
|
|
||||||
for (std::list<std::string>::const_iterator dirIt = scriptDirList.cbegin();
|
for (std::list<std::string>::const_iterator dirIt = scriptDirList.cbegin();
|
||||||
dirIt != scriptDirList.cend(); dirIt++) {
|
dirIt != scriptDirList.cend(); dirIt++) {
|
||||||
std::list<std::string> scripts = Utils::FileSystem::getDirContent(*dirIt);
|
std::list<std::string> scripts = Utils::FileSystem::getDirContent(*dirIt);
|
||||||
for (std::list<std::string>::const_iterator it = scripts.cbegin();
|
for (std::list<std::string>::const_iterator it = scripts.cbegin(); // Line break.
|
||||||
it != scripts.cend(); it++) {
|
it != scripts.cend(); it++) {
|
||||||
std::string arg1Quotation;
|
std::string arg1Quotation;
|
||||||
std::string arg2Quotation;
|
std::string arg2Quotation;
|
||||||
// Add quotation marks around the arguments as long as these are not already
|
// Add quotation marks around the arguments as long as these are not already
|
||||||
|
@ -51,10 +51,10 @@ namespace Scripting
|
||||||
if (arg2.front() != '\"')
|
if (arg2.front() != '\"')
|
||||||
arg2Quotation = "\"";
|
arg2Quotation = "\"";
|
||||||
std::string script = *it + " " + arg1Quotation + arg1 + arg1Quotation + " " +
|
std::string script = *it + " " + arg1Quotation + arg1 + arg1Quotation + " " +
|
||||||
arg2Quotation + arg2 + arg2Quotation;
|
arg2Quotation + arg2 + arg2Quotation;
|
||||||
LOG(LogDebug) << "Executing: " << script;
|
LOG(LogDebug) << "Executing: " << script;
|
||||||
runSystemCommand(script);
|
runSystemCommand(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} // namespace Scripting
|
||||||
|
|
|
@ -20,7 +20,8 @@
|
||||||
namespace Scripting
|
namespace Scripting
|
||||||
{
|
{
|
||||||
void fireEvent(const std::string& eventName,
|
void fireEvent(const std::string& eventName,
|
||||||
const std::string& arg1="", const std::string& arg2="");
|
const std::string& arg1 = "",
|
||||||
|
const std::string& arg2 = "");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //ES_CORE_SCRIPTING_H
|
#endif // ES_CORE_SCRIPTING_H
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#include "utils/FileSystemUtil.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "Scripting.h"
|
#include "Scripting.h"
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
@ -25,7 +25,9 @@ Settings* Settings::sInstance = nullptr;
|
||||||
// the in-program settings menu. Most can be set using command-line arguments,
|
// the in-program settings menu. Most can be set using command-line arguments,
|
||||||
// but some are debug flags that are either hardcoded or set by internal debug
|
// but some are debug flags that are either hardcoded or set by internal debug
|
||||||
// functions.
|
// functions.
|
||||||
std::vector<std::string> settings_dont_save {
|
std::vector<std::string> settingsSkipSaving
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
// These options can be set using command-line arguments:
|
// These options can be set using command-line arguments:
|
||||||
"WindowWidth", // Set via --resolution [width] [height]
|
"WindowWidth", // Set via --resolution [width] [height]
|
||||||
"WindowHeight", // set via --resolution [width] [height]
|
"WindowHeight", // set via --resolution [width] [height]
|
||||||
|
@ -55,6 +57,7 @@ std::vector<std::string> settings_dont_save {
|
||||||
"DebugImage",
|
"DebugImage",
|
||||||
"SplashScreenProgress",
|
"SplashScreenProgress",
|
||||||
"ScraperFilter"
|
"ScraperFilter"
|
||||||
|
// clang-format on
|
||||||
};
|
};
|
||||||
|
|
||||||
Settings::Settings()
|
Settings::Settings()
|
||||||
|
@ -174,9 +177,8 @@ void Settings::setDefaults()
|
||||||
mBoolMap["ScreensaverSlideshowScanlines"] = { true, true };
|
mBoolMap["ScreensaverSlideshowScanlines"] = { true, true };
|
||||||
mBoolMap["ScreensaverSlideshowCustomImages"] = { false, false };
|
mBoolMap["ScreensaverSlideshowCustomImages"] = { false, false };
|
||||||
mBoolMap["ScreensaverSlideshowRecurse"] = { false, false };
|
mBoolMap["ScreensaverSlideshowRecurse"] = { false, false };
|
||||||
mStringMap["ScreensaverSlideshowImageDir"] = {
|
mStringMap["ScreensaverSlideshowImageDir"] = { "~/.emulationstation/slideshow/custom_images",
|
||||||
"~/.emulationstation/slideshow/custom_images",
|
"~/.emulationstation/slideshow/custom_images" };
|
||||||
"~/.emulationstation/slideshow/custom_images" };
|
|
||||||
|
|
||||||
// UI settings -> screensaver settings -> video screensaver settings.
|
// UI settings -> screensaver settings -> video screensaver settings.
|
||||||
mIntMap["ScreensaverSwapVideoTimeout"] = { 0, 0 };
|
mIntMap["ScreensaverSwapVideoTimeout"] = { 0, 0 };
|
||||||
|
@ -185,6 +187,7 @@ void Settings::setDefaults()
|
||||||
mBoolMap["ScreensaverVideoScanlines"] = { true, true };
|
mBoolMap["ScreensaverVideoScanlines"] = { true, true };
|
||||||
mBoolMap["ScreensaverVideoBlur"] = { false, false };
|
mBoolMap["ScreensaverVideoBlur"] = { false, false };
|
||||||
|
|
||||||
|
#if defined(_RPI_)
|
||||||
// Sound settings.
|
// Sound settings.
|
||||||
// The ALSA Audio Card and Audio Device selection code is disabled at the moment.
|
// The ALSA Audio Card and Audio Device selection code is disabled at the moment.
|
||||||
// As PulseAudio controls the sound devices for the desktop environment, it doesn't
|
// As PulseAudio controls the sound devices for the desktop environment, it doesn't
|
||||||
|
@ -193,7 +196,6 @@ void Settings::setDefaults()
|
||||||
// settings could be added later on, if needed.
|
// settings could be added later on, if needed.
|
||||||
// The code is still active for Raspberry Pi though as I'm not sure if this is
|
// The code is still active for Raspberry Pi though as I'm not sure if this is
|
||||||
// useful for that device.
|
// useful for that device.
|
||||||
#if defined(_RPI_)
|
|
||||||
mStringMap["AudioCard"] = { "default", "default" };
|
mStringMap["AudioCard"] = { "default", "default" };
|
||||||
// Audio out device for volume control.
|
// Audio out device for volume control.
|
||||||
//#endif
|
//#endif
|
||||||
|
@ -201,9 +203,8 @@ void Settings::setDefaults()
|
||||||
mStringMap["AudioDevice"] = { "PCM", "PCM" };
|
mStringMap["AudioDevice"] = { "PCM", "PCM" };
|
||||||
// Audio out device for Video playback using OMX player.
|
// Audio out device for Video playback using OMX player.
|
||||||
mStringMap["OMXAudioDev"] = { "both", "both" };
|
mStringMap["OMXAudioDev"] = { "both", "both" };
|
||||||
//#else
|
#endif
|
||||||
// mStringMap["AudioDevice"] = { "Master", "Master" };
|
|
||||||
#endif
|
|
||||||
mIntMap["SoundVolumeNavigation"] = { 80, 80 };
|
mIntMap["SoundVolumeNavigation"] = { 80, 80 };
|
||||||
mIntMap["SoundVolumeVideos"] = { 100, 100 };
|
mIntMap["SoundVolumeVideos"] = { 100, 100 };
|
||||||
mBoolMap["GamelistVideoAudio"] = { true, true };
|
mBoolMap["GamelistVideoAudio"] = { true, true };
|
||||||
|
@ -223,32 +224,32 @@ void Settings::setDefaults()
|
||||||
mBoolMap["UseCustomCollectionsSystem"] = { true, true };
|
mBoolMap["UseCustomCollectionsSystem"] = { true, true };
|
||||||
mBoolMap["CollectionShowSystemInfo"] = { true, true };
|
mBoolMap["CollectionShowSystemInfo"] = { true, true };
|
||||||
|
|
||||||
// Other settings.
|
// Other settings.
|
||||||
#if defined(_RPI_)
|
#if defined(_RPI_)
|
||||||
mIntMap["MaxVRAM"] = { 80, 80 };
|
mIntMap["MaxVRAM"] = { 80, 80 };
|
||||||
#else
|
#else
|
||||||
mIntMap["MaxVRAM"] = { 256, 256 };
|
mIntMap["MaxVRAM"] = { 256, 256 };
|
||||||
#endif
|
#endif
|
||||||
mIntMap["DisplayIndex"] = { 1, 1 };
|
mIntMap["DisplayIndex"] = { 1, 1 };
|
||||||
#if defined (__unix__)
|
#if defined(__unix__)
|
||||||
mStringMap["FullscreenMode"] = { "normal", "normal" };
|
mStringMap["FullscreenMode"] = { "normal", "normal" };
|
||||||
#endif
|
#endif
|
||||||
#if defined(BUILD_VLC_PLAYER)
|
#if defined(BUILD_VLC_PLAYER)
|
||||||
mStringMap["VideoPlayer"] = { "ffmpeg", "ffmpeg" };
|
mStringMap["VideoPlayer"] = { "ffmpeg", "ffmpeg" };
|
||||||
#endif
|
#endif
|
||||||
#if defined(_RPI_)
|
#if defined(_RPI_)
|
||||||
mBoolMap["VideoOmxPlayer"] = { false, false };
|
mBoolMap["VideoOmxPlayer"] = { false, false };
|
||||||
// We're defaulting to OMX Player for full screen video on the Pi.
|
// We're defaulting to OMX Player for full screen video on the Pi.
|
||||||
mBoolMap["ScreensaverOmxPlayer"] = { true, true };
|
mBoolMap["ScreensaverOmxPlayer"] = { true, true };
|
||||||
#endif
|
#endif
|
||||||
mStringMap["SaveGamelistsMode"] = { "always", "always" };
|
mStringMap["SaveGamelistsMode"] = { "always", "always" };
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
mBoolMap["HideTaskbar"] = { false, false };
|
mBoolMap["HideTaskbar"] = { false, false };
|
||||||
#endif
|
#endif
|
||||||
mBoolMap["RunInBackground"] = { false, false };
|
mBoolMap["RunInBackground"] = { false, false };
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
mBoolMap["LaunchWorkaround"] = { true, true };
|
mBoolMap["LaunchWorkaround"] = { true, true };
|
||||||
#endif
|
#endif
|
||||||
mStringMap["MediaDirectory"] = { "", "" };
|
mStringMap["MediaDirectory"] = { "", "" };
|
||||||
mBoolMap["VideoUpscaleFrameRate"] = { false, false };
|
mBoolMap["VideoUpscaleFrameRate"] = { false, false };
|
||||||
mBoolMap["LaunchCommandOverride"] = { true, true };
|
mBoolMap["LaunchCommandOverride"] = { true, true };
|
||||||
|
@ -256,15 +257,15 @@ void Settings::setDefaults()
|
||||||
mBoolMap["ShowHiddenGames"] = { true, true };
|
mBoolMap["ShowHiddenGames"] = { true, true };
|
||||||
mBoolMap["CustomEventScripts"] = { false, false };
|
mBoolMap["CustomEventScripts"] = { false, false };
|
||||||
mBoolMap["ParseGamelistOnly"] = { false, false };
|
mBoolMap["ParseGamelistOnly"] = { false, false };
|
||||||
#if defined(__unix__)
|
#if defined(__unix__)
|
||||||
mBoolMap["DisableComposition"] = { true, true };
|
mBoolMap["DisableComposition"] = { true, true };
|
||||||
#endif
|
#endif
|
||||||
mBoolMap["DisplayGPUStatistics"] = { false, false };
|
mBoolMap["DisplayGPUStatistics"] = { false, false };
|
||||||
// macOS requires root privileges to reboot and power off so it doesn't make much
|
// macOS requires root privileges to reboot and power off so it doesn't make much
|
||||||
// sense to enable this setting and menu entry for that operating system.
|
// sense to enable this setting and menu entry for that operating system.
|
||||||
#if !defined(__APPLE__)
|
#if !defined(__APPLE__)
|
||||||
mBoolMap["ShowQuitMenu"] = { false, false };
|
mBoolMap["ShowQuitMenu"] = { false, false };
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//
|
//
|
||||||
// Settings configured via command-line arguments.
|
// Settings configured via command-line arguments.
|
||||||
|
@ -278,18 +279,18 @@ void Settings::setDefaults()
|
||||||
mBoolMap["IgnoreGamelist"] = { false, false };
|
mBoolMap["IgnoreGamelist"] = { false, false };
|
||||||
mBoolMap["SplashScreen"] = { true, true };
|
mBoolMap["SplashScreen"] = { true, true };
|
||||||
mBoolMap["VSync"] = { true, true };
|
mBoolMap["VSync"] = { true, true };
|
||||||
#if !defined(_WIN64)
|
#if !defined(_WIN64)
|
||||||
mBoolMap["Windowed"] = { false, false };
|
mBoolMap["Windowed"] = { false, false };
|
||||||
#endif
|
#endif
|
||||||
mIntMap["WindowWidth"] = { 0, 0 };
|
mIntMap["WindowWidth"] = { 0, 0 };
|
||||||
mIntMap["WindowHeight"] = { 0, 0 };
|
mIntMap["WindowHeight"] = { 0, 0 };
|
||||||
mIntMap["ScreenWidth"] = { 0, 0 };
|
mIntMap["ScreenWidth"] = { 0, 0 };
|
||||||
|
|
||||||
// Undocumented options.
|
// Undocumented options.
|
||||||
mIntMap["ScreenHeight"] = { 0, 0 };
|
mIntMap["ScreenHeight"] = { 0, 0 };
|
||||||
mIntMap["ScreenOffsetX"] = { 0, 0 };
|
mIntMap["ScreenOffsetX"] = { 0, 0 };
|
||||||
mIntMap["ScreenOffsetY"] = { 0, 0 };
|
mIntMap["ScreenOffsetY"] = { 0, 0 };
|
||||||
mIntMap["ScreenRotate"] = { 0, 0 };
|
mIntMap["ScreenRotate"] = { 0, 0 };
|
||||||
|
|
||||||
//
|
//
|
||||||
// Settings that can be changed in es_settings.xml
|
// Settings that can be changed in es_settings.xml
|
||||||
|
@ -317,9 +318,10 @@ void saveMap(pugi::xml_document& doc, std::map<K, V>& map, const std::string& ty
|
||||||
{
|
{
|
||||||
for (auto iter = map.cbegin(); iter != map.cend(); iter++) {
|
for (auto iter = map.cbegin(); iter != map.cend(); iter++) {
|
||||||
// Key is on the "don't save" list, so don't save it.
|
// Key is on the "don't save" list, so don't save it.
|
||||||
if (std::find(settings_dont_save.cbegin(), settings_dont_save.cend(),
|
if (std::find(settingsSkipSaving.cbegin(), settingsSkipSaving.cend(), iter->first) !=
|
||||||
iter->first) != settings_dont_save.cend())
|
settingsSkipSaving.cend()) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
pugi::xml_node node = doc.append_child(type.c_str());
|
pugi::xml_node node = doc.append_child(type.c_str());
|
||||||
node.append_attribute("name").set_value(iter->first.c_str());
|
node.append_attribute("name").set_value(iter->first.c_str());
|
||||||
|
@ -330,8 +332,8 @@ void saveMap(pugi::xml_document& doc, std::map<K, V>& map, const std::string& ty
|
||||||
void Settings::saveFile()
|
void Settings::saveFile()
|
||||||
{
|
{
|
||||||
LOG(LogDebug) << "Settings::saveFile(): Saving settings to es_settings.xml";
|
LOG(LogDebug) << "Settings::saveFile(): Saving settings to es_settings.xml";
|
||||||
const std::string path = Utils::FileSystem::getHomePath() +
|
const std::string path =
|
||||||
"/.emulationstation/es_settings.xml";
|
Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml";
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
|
|
||||||
|
@ -345,11 +347,11 @@ void Settings::saveFile()
|
||||||
node.append_attribute("value").set_value(iter->second.second.c_str());
|
node.append_attribute("value").set_value(iter->second.second.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
doc.save_file(Utils::String::stringToWideString(path).c_str());
|
doc.save_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
doc.save_file(path.c_str());
|
doc.save_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Scripting::fireEvent("config-changed");
|
Scripting::fireEvent("config-changed");
|
||||||
Scripting::fireEvent("settings-changed");
|
Scripting::fireEvent("settings-changed");
|
||||||
|
@ -358,11 +360,11 @@ void Settings::saveFile()
|
||||||
void Settings::loadFile()
|
void Settings::loadFile()
|
||||||
{
|
{
|
||||||
// Prior to ES-DE v1.1, the configuration file had the .cfg suffix instead of .xml
|
// Prior to ES-DE v1.1, the configuration file had the .cfg suffix instead of .xml
|
||||||
const std::string legacyConfigFile = Utils::FileSystem::getHomePath() +
|
const std::string legacyConfigFile =
|
||||||
"/.emulationstation/es_settings.cfg";
|
Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.cfg";
|
||||||
|
|
||||||
const std::string configFile = Utils::FileSystem::getHomePath() +
|
const std::string configFile =
|
||||||
"/.emulationstation/es_settings.xml";
|
Utils::FileSystem::getHomePath() + "/.emulationstation/es_settings.xml";
|
||||||
|
|
||||||
if (Utils::FileSystem::exists(legacyConfigFile) && !Utils::FileSystem::exists(configFile))
|
if (Utils::FileSystem::exists(legacyConfigFile) && !Utils::FileSystem::exists(configFile))
|
||||||
Utils::FileSystem::copyFile(legacyConfigFile, configFile, false);
|
Utils::FileSystem::copyFile(legacyConfigFile, configFile, false);
|
||||||
|
@ -371,12 +373,12 @@ void Settings::loadFile()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result =
|
||||||
doc.load_file(Utils::String::stringToWideString(configFile).c_str());
|
doc.load_file(Utils::String::stringToWideString(configFile).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = doc.load_file(configFile.c_str());
|
pugi::xml_parse_result result = doc.load_file(configFile.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (!result) {
|
if (!result) {
|
||||||
LOG(LogError) << "Could not parse the es_settings.xml file\n " << result.description();
|
LOG(LogError) << "Could not parse the es_settings.xml file\n " << result.description();
|
||||||
return;
|
return;
|
||||||
|
@ -392,37 +394,37 @@ void Settings::loadFile()
|
||||||
setString(node.attribute("name").as_string(), node.attribute("value").as_string());
|
setString(node.attribute("name").as_string(), node.attribute("value").as_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print a warning message if the setting we're trying to get doesn't already exist in
|
// Macro to create the get and set functions for the various data types
|
||||||
// the map. Then return the value in the map.
|
#define SETTINGS_GETSET(type, mapName, getFunction, getDefaultFunction, setFunction) \
|
||||||
#define SETTINGS_GETSET(type, mapName, getFunction, getDefaultFunction, setFunction) \
|
type Settings::getFunction(const std::string& name) \
|
||||||
type Settings::getFunction(const std::string& name) \
|
{ \
|
||||||
{ \
|
if (mapName.find(name) == mapName.cend()) { \
|
||||||
if (mapName.find(name) == mapName.cend()) { \
|
LOG(LogError) << "Tried to use unset setting " << name; \
|
||||||
LOG(LogError) << "Tried to use unset setting " << name; \
|
} \
|
||||||
} \
|
return mapName[name].second; \
|
||||||
return mapName[name].second; \
|
} \
|
||||||
} \
|
type Settings::getDefaultFunction(const std::string& name) \
|
||||||
type Settings::getDefaultFunction(const std::string& name) \
|
{ \
|
||||||
{ \
|
if (mapName.find(name) == mapName.cend()) { \
|
||||||
if (mapName.find(name) == mapName.cend()) { \
|
LOG(LogError) << "Tried to use unset setting " << name; \
|
||||||
LOG(LogError) << "Tried to use unset setting " << name; \
|
} \
|
||||||
} \
|
return mapName[name].first; \
|
||||||
return mapName[name].first; \
|
} \
|
||||||
} \
|
bool Settings::setFunction(const std::string& name, type value) \
|
||||||
bool Settings::setFunction(const std::string& name, type value) \
|
{ \
|
||||||
{ \
|
if (mapName.count(name) == 0 || mapName[name].second != value) { \
|
||||||
if (mapName.count(name) == 0 || mapName[name].second != value) { \
|
mapName[name].second = value; \
|
||||||
mapName[name].second = value; \
|
\
|
||||||
\
|
if (std::find(settingsSkipSaving.cbegin(), settingsSkipSaving.cend(), name) == \
|
||||||
if (std::find(settings_dont_save.cbegin(), settings_dont_save.cend(), name) \
|
settingsSkipSaving.cend()) \
|
||||||
== settings_dont_save.cend()) \
|
mWasChanged = true; \
|
||||||
mWasChanged = true; \
|
\
|
||||||
\
|
return true; \
|
||||||
return true; \
|
} \
|
||||||
} \
|
return false; \
|
||||||
return false; \
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
// Parameters for the macro defined above.
|
||||||
SETTINGS_GETSET(bool, mBoolMap, getBool, getDefaultBool, setBool);
|
SETTINGS_GETSET(bool, mBoolMap, getBool, getDefaultBool, setBool);
|
||||||
SETTINGS_GETSET(int, mIntMap, getInt, getDefaultInt, setInt);
|
SETTINGS_GETSET(int, mIntMap, getInt, getDefaultInt, setInt);
|
||||||
SETTINGS_GETSET(float, mFloatMap, getFloat, getDefaultFloat, setFloat);
|
SETTINGS_GETSET(float, mFloatMap, getFloat, getDefaultFloat, setFloat);
|
||||||
|
|
|
@ -9,11 +9,11 @@
|
||||||
|
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
#include "resources/ResourceManager.h"
|
||||||
|
|
||||||
NavigationSounds* NavigationSounds::sInstance = nullptr;
|
NavigationSounds* NavigationSounds::sInstance = nullptr;
|
||||||
|
|
||||||
|
@ -32,11 +32,12 @@ std::shared_ptr<Sound> Sound::get(const std::string& path)
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element)
|
const std::string& view,
|
||||||
|
const std::string& element)
|
||||||
{
|
{
|
||||||
if (!theme) {
|
if (!theme) {
|
||||||
LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \""
|
LOG(LogDebug) << "Sound::getFromTheme(): Using fallback sound file for \"" << element
|
||||||
<< element << "\"";
|
<< "\"";
|
||||||
return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav"));
|
return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +45,7 @@ std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& the
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "sound");
|
||||||
if (!elem || !elem->has("path")) {
|
if (!elem || !elem->has("path")) {
|
||||||
LOG(LogDebug) << "Sound::getFromTheme(): " << "Tag not found, using fallback sound file";
|
LOG(LogDebug) << "Sound::getFromTheme(): Tag not found, using fallback sound file";
|
||||||
return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav"));
|
return get(ResourceManager::getInstance()->getResourcePath(":/sounds/" + element + ".wav"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,20 +53,15 @@ std::shared_ptr<Sound> Sound::getFromTheme(const std::shared_ptr<ThemeData>& the
|
||||||
return get(elem->get<std::string>("path"));
|
return get(elem->get<std::string>("path"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound::Sound(
|
Sound::Sound(const std::string& path)
|
||||||
const std::string& path)
|
: mSampleData(nullptr)
|
||||||
: mSampleData(nullptr),
|
, mSamplePos(0)
|
||||||
mSamplePos(0),
|
, mSampleLength(0)
|
||||||
mSampleLength(0),
|
, playing(false)
|
||||||
playing(false)
|
|
||||||
{
|
{
|
||||||
loadFile(path);
|
loadFile(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
Sound::~Sound()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sound::loadFile(const std::string& path)
|
void Sound::loadFile(const std::string& path)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
|
@ -91,9 +87,9 @@ void Sound::init()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert sound file to the format required by ES-DE.
|
// Convert sound file to the format required by ES-DE.
|
||||||
SDL_AudioStream *conversionStream = SDL_NewAudioStream(wave.format, wave.channels, wave.freq,
|
SDL_AudioStream* conversionStream =
|
||||||
AudioManager::sAudioFormat.format, AudioManager::sAudioFormat.channels,
|
SDL_NewAudioStream(wave.format, wave.channels, wave.freq, AudioManager::sAudioFormat.format,
|
||||||
AudioManager::sAudioFormat.freq);
|
AudioManager::sAudioFormat.channels, AudioManager::sAudioFormat.freq);
|
||||||
|
|
||||||
if (conversionStream == nullptr) {
|
if (conversionStream == nullptr) {
|
||||||
LOG(LogError) << "Failed to create sample conversion stream:";
|
LOG(LogError) << "Failed to create sample conversion stream:";
|
||||||
|
@ -169,11 +165,6 @@ void Sound::play()
|
||||||
AudioManager::getInstance()->play();
|
AudioManager::getInstance()->play();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Sound::isPlaying() const
|
|
||||||
{
|
|
||||||
return playing;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sound::stop()
|
void Sound::stop()
|
||||||
{
|
{
|
||||||
// Flag our sample as not playing and rewind its position.
|
// Flag our sample as not playing and rewind its position.
|
||||||
|
@ -183,16 +174,6 @@ void Sound::stop()
|
||||||
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
SDL_UnlockAudioDevice(AudioManager::sAudioDevice);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Uint8* Sound::getData() const
|
|
||||||
{
|
|
||||||
return mSampleData;
|
|
||||||
}
|
|
||||||
|
|
||||||
Uint32 Sound::getPosition() const
|
|
||||||
{
|
|
||||||
return mSamplePos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Sound::setPosition(Uint32 newPosition)
|
void Sound::setPosition(Uint32 newPosition)
|
||||||
{
|
{
|
||||||
mSamplePos = newPosition;
|
mSamplePos = newPosition;
|
||||||
|
@ -203,11 +184,6 @@ void Sound::setPosition(Uint32 newPosition)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Uint32 Sound::getLength() const
|
|
||||||
{
|
|
||||||
return mSampleLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationSounds* NavigationSounds::getInstance()
|
NavigationSounds* NavigationSounds::getInstance()
|
||||||
{
|
{
|
||||||
if (sInstance == nullptr)
|
if (sInstance == nullptr)
|
||||||
|
@ -234,11 +210,12 @@ void NavigationSounds::loadThemeNavigationSounds(const std::shared_ptr<ThemeData
|
||||||
{
|
{
|
||||||
if (theme) {
|
if (theme) {
|
||||||
LOG(LogDebug) << "NavigationSounds::loadThemeNavigationSounds(): "
|
LOG(LogDebug) << "NavigationSounds::loadThemeNavigationSounds(): "
|
||||||
"Theme set includes navigation sound support, loading custom sounds";
|
"Theme set includes navigation sound support, loading custom sounds";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogDebug) << "NavigationSounds::loadThemeNavigationSounds(): "
|
LOG(LogDebug)
|
||||||
"Theme set does not include navigation sound support, using fallback sounds";
|
<< "NavigationSounds::loadThemeNavigationSounds(): "
|
||||||
|
"Theme set does not include navigation sound support, using fallback sounds";
|
||||||
}
|
}
|
||||||
|
|
||||||
navigationSounds.push_back(std::move(Sound::getFromTheme(theme, "all", "systembrowse")));
|
navigationSounds.push_back(std::move(Sound::getFromTheme(theme, "all", "systembrowse")));
|
||||||
|
|
|
@ -22,7 +22,7 @@ class ThemeData;
|
||||||
class Sound
|
class Sound
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
~Sound();
|
~Sound() {}
|
||||||
|
|
||||||
void init();
|
void init();
|
||||||
void deinit();
|
void deinit();
|
||||||
|
@ -30,17 +30,18 @@ public:
|
||||||
void loadFile(const std::string& path);
|
void loadFile(const std::string& path);
|
||||||
|
|
||||||
void play();
|
void play();
|
||||||
bool isPlaying() const;
|
bool isPlaying() const { return playing; }
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
const Uint8* getData() const;
|
const Uint8* getData() const { return mSampleData; }
|
||||||
Uint32 getPosition() const;
|
Uint32 getPosition() const { return mSamplePos; }
|
||||||
void setPosition(Uint32 newPosition);
|
void setPosition(Uint32 newPosition);
|
||||||
Uint32 getLength() const;
|
Uint32 getLength() const { return mSampleLength; }
|
||||||
|
|
||||||
static std::shared_ptr<Sound> get(const std::string& path);
|
static std::shared_ptr<Sound> get(const std::string& path);
|
||||||
static std::shared_ptr<Sound> getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
static std::shared_ptr<Sound> getFromTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& elem);
|
const std::string& view,
|
||||||
|
const std::string& elem);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Sound(const std::string& path = "");
|
Sound(const std::string& path = "");
|
||||||
|
|
|
@ -10,190 +10,190 @@
|
||||||
|
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Platform.h"
|
||||||
|
#include "Settings.h"
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Log.h"
|
|
||||||
#include "Platform.h"
|
|
||||||
#include "Settings.h"
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
|
|
||||||
std::vector<std::string> ThemeData::sSupportedViews {
|
std::vector<std::string> ThemeData::sSupportedViews { { "all" }, { "system" }, { "basic" },
|
||||||
{ "all" }, { "system" }, { "basic" }, { "detailed" }, { "grid" }, { "video" } };
|
{ "detailed" }, { "grid" }, { "video" } };
|
||||||
std::vector<std::string> ThemeData::sSupportedFeatures {
|
std::vector<std::string> ThemeData::sSupportedFeatures {
|
||||||
{ "navigationsounds" }, { "video" }, { "carousel" }, { "z-index" }, { "visible" } };
|
{ "navigationsounds" }, { "video" }, { "carousel" }, { "z-index" }, { "visible" }
|
||||||
|
|
||||||
std::map<std::string, std::map<std::string,
|
|
||||||
ThemeData::ElementPropertyType>> ThemeData::sElementMap {
|
|
||||||
{ "image", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "maxSize", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "rotation", FLOAT },
|
|
||||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "path", PATH },
|
|
||||||
{ "default", PATH },
|
|
||||||
{ "tile", BOOLEAN },
|
|
||||||
{ "color", COLOR },
|
|
||||||
{ "colorEnd", COLOR },
|
|
||||||
{ "gradientType", STRING },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "imagegrid", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "margin", NORMALIZED_PAIR },
|
|
||||||
{ "padding", NORMALIZED_RECT },
|
|
||||||
{ "autoLayout", NORMALIZED_PAIR },
|
|
||||||
{ "autoLayoutSelectedZoom", FLOAT },
|
|
||||||
{ "gameImage", PATH },
|
|
||||||
{ "folderImage", PATH },
|
|
||||||
{ "imageSource", STRING },
|
|
||||||
{ "scrollDirection", STRING },
|
|
||||||
{ "centerSelection", BOOLEAN },
|
|
||||||
{ "scrollLoop", BOOLEAN },
|
|
||||||
{ "animate", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "gridtile", {
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "padding", NORMALIZED_PAIR },
|
|
||||||
{ "imageColor", COLOR },
|
|
||||||
{ "backgroundImage", PATH },
|
|
||||||
{ "backgroundCornerSize", NORMALIZED_PAIR },
|
|
||||||
{ "backgroundColor", COLOR },
|
|
||||||
{ "backgroundCenterColor", COLOR },
|
|
||||||
{ "backgroundEdgeColor", COLOR } } },
|
|
||||||
{ "text", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "rotation", FLOAT },
|
|
||||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "text", STRING },
|
|
||||||
{ "backgroundColor", COLOR },
|
|
||||||
{ "fontPath", PATH },
|
|
||||||
{ "fontSize", FLOAT },
|
|
||||||
{ "color", COLOR },
|
|
||||||
{ "alignment", STRING },
|
|
||||||
{ "forceUppercase", BOOLEAN },
|
|
||||||
{ "lineSpacing", FLOAT },
|
|
||||||
{ "value", STRING },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "textlist", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "selectorHeight", FLOAT },
|
|
||||||
{ "selectorOffsetY", FLOAT },
|
|
||||||
{ "selectorColor", COLOR },
|
|
||||||
{ "selectorColorEnd", COLOR },
|
|
||||||
{ "selectorGradientType", STRING },
|
|
||||||
{ "selectorImagePath", PATH },
|
|
||||||
{ "selectorImageTile", BOOLEAN },
|
|
||||||
{ "selectedColor", COLOR },
|
|
||||||
{ "primaryColor", COLOR },
|
|
||||||
{ "secondaryColor", COLOR },
|
|
||||||
{ "fontPath", PATH },
|
|
||||||
{ "fontSize", FLOAT },
|
|
||||||
{ "scrollSound", PATH }, // Need to keep this for backward compatibility with old themes.
|
|
||||||
{ "alignment", STRING },
|
|
||||||
{ "horizontalMargin", FLOAT },
|
|
||||||
{ "forceUppercase", BOOLEAN },
|
|
||||||
{ "lineSpacing", FLOAT },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "container", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "ninepatch", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "path", PATH },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "datetime", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "rotation", FLOAT },
|
|
||||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "backgroundColor", COLOR },
|
|
||||||
{ "fontPath", PATH },
|
|
||||||
{ "fontSize", FLOAT },
|
|
||||||
{ "color", COLOR },
|
|
||||||
{ "alignment", STRING },
|
|
||||||
{ "forceUppercase", BOOLEAN },
|
|
||||||
{ "lineSpacing", FLOAT },
|
|
||||||
{ "value", STRING },
|
|
||||||
{ "format", STRING },
|
|
||||||
{ "displayRelative", BOOLEAN },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "rating", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "rotation", FLOAT },
|
|
||||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "color", COLOR },
|
|
||||||
{ "filledPath", PATH },
|
|
||||||
{ "unfilledPath", PATH },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT } } },
|
|
||||||
{ "sound", {
|
|
||||||
{ "path", PATH } } },
|
|
||||||
{ "helpsystem", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "textColor", COLOR },
|
|
||||||
{ "iconColor", COLOR },
|
|
||||||
{ "fontPath", PATH },
|
|
||||||
{ "fontSize", FLOAT } } },
|
|
||||||
{ "navigationsounds", {
|
|
||||||
{ "systembrowseSound", PATH },
|
|
||||||
{ "quicksysselectSound", PATH },
|
|
||||||
{ "selectSound", PATH },
|
|
||||||
{ "backSound", PATH },
|
|
||||||
{ "scrollSound", PATH },
|
|
||||||
{ "favoriteSound", PATH },
|
|
||||||
{ "launchSound", PATH } } },
|
|
||||||
{ "video", {
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "maxSize", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "rotation", FLOAT },
|
|
||||||
{ "rotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "default", PATH },
|
|
||||||
{ "delay", FLOAT },
|
|
||||||
{ "visible", BOOLEAN },
|
|
||||||
{ "zIndex", FLOAT },
|
|
||||||
{ "showSnapshotNoVideo", BOOLEAN },
|
|
||||||
{ "showSnapshotDelay", BOOLEAN } } },
|
|
||||||
{ "carousel", {
|
|
||||||
{ "type", STRING },
|
|
||||||
{ "size", NORMALIZED_PAIR },
|
|
||||||
{ "pos", NORMALIZED_PAIR },
|
|
||||||
{ "origin", NORMALIZED_PAIR },
|
|
||||||
{ "color", COLOR },
|
|
||||||
{ "colorEnd", COLOR },
|
|
||||||
{ "gradientType", STRING },
|
|
||||||
{ "logoScale", FLOAT },
|
|
||||||
{ "logoRotation", FLOAT },
|
|
||||||
{ "logoRotationOrigin", NORMALIZED_PAIR },
|
|
||||||
{ "logoSize", NORMALIZED_PAIR },
|
|
||||||
{ "logoAlignment", STRING },
|
|
||||||
{ "maxLogoCount", FLOAT },
|
|
||||||
{ "zIndex", FLOAT } } }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
|
||||||
|
ThemeData::sElementMap {
|
||||||
|
{ "image",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "maxSize", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "rotation", FLOAT },
|
||||||
|
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "path", PATH },
|
||||||
|
{ "default", PATH },
|
||||||
|
{ "tile", BOOLEAN },
|
||||||
|
{ "color", COLOR },
|
||||||
|
{ "colorEnd", COLOR },
|
||||||
|
{ "gradientType", STRING },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "imagegrid",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "margin", NORMALIZED_PAIR },
|
||||||
|
{ "padding", NORMALIZED_RECT },
|
||||||
|
{ "autoLayout", NORMALIZED_PAIR },
|
||||||
|
{ "autoLayoutSelectedZoom", FLOAT },
|
||||||
|
{ "gameImage", PATH },
|
||||||
|
{ "folderImage", PATH },
|
||||||
|
{ "imageSource", STRING },
|
||||||
|
{ "scrollDirection", STRING },
|
||||||
|
{ "centerSelection", BOOLEAN },
|
||||||
|
{ "scrollLoop", BOOLEAN },
|
||||||
|
{ "animate", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "gridtile",
|
||||||
|
{ { "size", NORMALIZED_PAIR },
|
||||||
|
{ "padding", NORMALIZED_PAIR },
|
||||||
|
{ "imageColor", COLOR },
|
||||||
|
{ "backgroundImage", PATH },
|
||||||
|
{ "backgroundCornerSize", NORMALIZED_PAIR },
|
||||||
|
{ "backgroundColor", COLOR },
|
||||||
|
{ "backgroundCenterColor", COLOR },
|
||||||
|
{ "backgroundEdgeColor", COLOR } } },
|
||||||
|
{ "text",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "rotation", FLOAT },
|
||||||
|
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "text", STRING },
|
||||||
|
{ "backgroundColor", COLOR },
|
||||||
|
{ "fontPath", PATH },
|
||||||
|
{ "fontSize", FLOAT },
|
||||||
|
{ "color", COLOR },
|
||||||
|
{ "alignment", STRING },
|
||||||
|
{ "forceUppercase", BOOLEAN },
|
||||||
|
{ "lineSpacing", FLOAT },
|
||||||
|
{ "value", STRING },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "textlist",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "selectorHeight", FLOAT },
|
||||||
|
{ "selectorOffsetY", FLOAT },
|
||||||
|
{ "selectorColor", COLOR },
|
||||||
|
{ "selectorColorEnd", COLOR },
|
||||||
|
{ "selectorGradientType", STRING },
|
||||||
|
{ "selectorImagePath", PATH },
|
||||||
|
{ "selectorImageTile", BOOLEAN },
|
||||||
|
{ "selectedColor", COLOR },
|
||||||
|
{ "primaryColor", COLOR },
|
||||||
|
{ "secondaryColor", COLOR },
|
||||||
|
{ "fontPath", PATH },
|
||||||
|
{ "fontSize", FLOAT },
|
||||||
|
{ "scrollSound", PATH }, // For backward compatibility with old themes.
|
||||||
|
{ "alignment", STRING },
|
||||||
|
{ "horizontalMargin", FLOAT },
|
||||||
|
{ "forceUppercase", BOOLEAN },
|
||||||
|
{ "lineSpacing", FLOAT },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "container",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "ninepatch",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "path", PATH },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "datetime",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "rotation", FLOAT },
|
||||||
|
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "backgroundColor", COLOR },
|
||||||
|
{ "fontPath", PATH },
|
||||||
|
{ "fontSize", FLOAT },
|
||||||
|
{ "color", COLOR },
|
||||||
|
{ "alignment", STRING },
|
||||||
|
{ "forceUppercase", BOOLEAN },
|
||||||
|
{ "lineSpacing", FLOAT },
|
||||||
|
{ "value", STRING },
|
||||||
|
{ "format", STRING },
|
||||||
|
{ "displayRelative", BOOLEAN },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "rating",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "rotation", FLOAT },
|
||||||
|
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "color", COLOR },
|
||||||
|
{ "filledPath", PATH },
|
||||||
|
{ "unfilledPath", PATH },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT } } },
|
||||||
|
{ "sound", { { "path", PATH } } },
|
||||||
|
{ "helpsystem",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "textColor", COLOR },
|
||||||
|
{ "iconColor", COLOR },
|
||||||
|
{ "fontPath", PATH },
|
||||||
|
{ "fontSize", FLOAT } } },
|
||||||
|
{ "navigationsounds",
|
||||||
|
{ { "systembrowseSound", PATH },
|
||||||
|
{ "quicksysselectSound", PATH },
|
||||||
|
{ "selectSound", PATH },
|
||||||
|
{ "backSound", PATH },
|
||||||
|
{ "scrollSound", PATH },
|
||||||
|
{ "favoriteSound", PATH },
|
||||||
|
{ "launchSound", PATH } } },
|
||||||
|
{ "video",
|
||||||
|
{ { "pos", NORMALIZED_PAIR },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "maxSize", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "rotation", FLOAT },
|
||||||
|
{ "rotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "default", PATH },
|
||||||
|
{ "delay", FLOAT },
|
||||||
|
{ "visible", BOOLEAN },
|
||||||
|
{ "zIndex", FLOAT },
|
||||||
|
{ "showSnapshotNoVideo", BOOLEAN },
|
||||||
|
{ "showSnapshotDelay", BOOLEAN } } },
|
||||||
|
{ "carousel",
|
||||||
|
{ { "type", STRING },
|
||||||
|
{ "size", NORMALIZED_PAIR },
|
||||||
|
{ "pos", NORMALIZED_PAIR },
|
||||||
|
{ "origin", NORMALIZED_PAIR },
|
||||||
|
{ "color", COLOR },
|
||||||
|
{ "colorEnd", COLOR },
|
||||||
|
{ "gradientType", STRING },
|
||||||
|
{ "logoScale", FLOAT },
|
||||||
|
{ "logoRotation", FLOAT },
|
||||||
|
{ "logoRotationOrigin", NORMALIZED_PAIR },
|
||||||
|
{ "logoSize", NORMALIZED_PAIR },
|
||||||
|
{ "logoAlignment", STRING },
|
||||||
|
{ "maxLogoCount", FLOAT },
|
||||||
|
{ "zIndex", FLOAT } } }
|
||||||
|
};
|
||||||
|
|
||||||
#define MINIMUM_THEME_FORMAT_VERSION 3
|
#define MINIMUM_THEME_FORMAT_VERSION 3
|
||||||
#define CURRENT_THEME_FORMAT_VERSION 6
|
#define CURRENT_THEME_FORMAT_VERSION 6
|
||||||
|
|
||||||
|
@ -241,6 +241,7 @@ std::string resolvePlaceholders(const std::string& in)
|
||||||
|
|
||||||
ThemeData::ThemeData()
|
ThemeData::ThemeData()
|
||||||
{
|
{
|
||||||
|
// The version will be loaded from the theme file.
|
||||||
mVersion = 0;
|
mVersion = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,11 +262,11 @@ void ThemeData::loadFile(std::map<std::string, std::string> sysDataMap, const st
|
||||||
mVariables.insert(sysDataMap.cbegin(), sysDataMap.cend());
|
mVariables.insert(sysDataMap.cbegin(), sysDataMap.cend());
|
||||||
|
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
|
pugi::xml_parse_result res = doc.load_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
pugi::xml_parse_result res = doc.load_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (!res)
|
if (!res)
|
||||||
throw error << "XML parsing error: \n " << res.description();
|
throw error << "XML parsing error: \n " << res.description();
|
||||||
|
|
||||||
|
@ -276,13 +277,13 @@ void ThemeData::loadFile(std::map<std::string, std::string> sysDataMap, const st
|
||||||
// parse version
|
// parse version
|
||||||
mVersion = root.child("formatVersion").text().as_float(-404);
|
mVersion = root.child("formatVersion").text().as_float(-404);
|
||||||
if (mVersion == -404)
|
if (mVersion == -404)
|
||||||
throw error << "<formatVersion> tag missing!\n It's either out of date or you need "
|
throw error << "<formatVersion> tag missing\n "
|
||||||
"to add <formatVersion>" << CURRENT_THEME_FORMAT_VERSION <<
|
"It's either out of date or you need to add <formatVersion>"
|
||||||
"</formatVersion> inside your <theme> tag.";
|
<< CURRENT_THEME_FORMAT_VERSION << "</formatVersion> inside your <theme> tag.";
|
||||||
|
|
||||||
if (mVersion < MINIMUM_THEME_FORMAT_VERSION)
|
if (mVersion < MINIMUM_THEME_FORMAT_VERSION)
|
||||||
throw error << "Theme uses format version " << mVersion <<
|
throw error << "Theme uses format version " << mVersion << ". Minimum supported version is "
|
||||||
". Minimum supported version is " << MINIMUM_THEME_FORMAT_VERSION << ".";
|
<< MINIMUM_THEME_FORMAT_VERSION << ".";
|
||||||
|
|
||||||
parseVariables(root);
|
parseVariables(root);
|
||||||
parseIncludes(root);
|
parseIncludes(root);
|
||||||
|
@ -299,19 +300,18 @@ void ThemeData::parseIncludes(const pugi::xml_node& root)
|
||||||
std::string relPath = resolvePlaceholders(node.text().as_string());
|
std::string relPath = resolvePlaceholders(node.text().as_string());
|
||||||
std::string path = Utils::FileSystem::resolveRelativePath(relPath, mPaths.back(), true);
|
std::string path = Utils::FileSystem::resolveRelativePath(relPath, mPaths.back(), true);
|
||||||
if (!ResourceManager::getInstance()->fileExists(path))
|
if (!ResourceManager::getInstance()->fileExists(path))
|
||||||
throw error << " -> \"" << relPath <<
|
throw error << " -> \"" << relPath << "\" not found (resolved to \"" << path << "\")";
|
||||||
"\" not found (resolved to \"" << path << "\")";
|
|
||||||
error << " -> \"" << relPath << "\"";
|
error << " -> \"" << relPath << "\"";
|
||||||
|
|
||||||
mPaths.push_back(path);
|
mPaths.push_back(path);
|
||||||
|
|
||||||
pugi::xml_document includeDoc;
|
pugi::xml_document includeDoc;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
pugi::xml_parse_result result =
|
pugi::xml_parse_result result =
|
||||||
includeDoc.load_file(Utils::String::stringToWideString(path).c_str());
|
includeDoc.load_file(Utils::String::stringToWideString(path).c_str());
|
||||||
#else
|
#else
|
||||||
pugi::xml_parse_result result = includeDoc.load_file(path.c_str());
|
pugi::xml_parse_result result = includeDoc.load_file(path.c_str());
|
||||||
#endif
|
#endif
|
||||||
if (!result)
|
if (!result)
|
||||||
throw error << ": Error parsing file: " << result.description();
|
throw error << ": Error parsing file: " << result.description();
|
||||||
|
|
||||||
|
@ -339,9 +339,10 @@ void ThemeData::parseFeatures(const pugi::xml_node& root)
|
||||||
|
|
||||||
const std::string supportedAttr = node.attribute("supported").as_string();
|
const std::string supportedAttr = node.attribute("supported").as_string();
|
||||||
|
|
||||||
if (std::find(sSupportedFeatures.cbegin(), sSupportedFeatures.cend(),
|
if (std::find(sSupportedFeatures.cbegin(), sSupportedFeatures.cend(), supportedAttr) !=
|
||||||
supportedAttr) != sSupportedFeatures.cend())
|
sSupportedFeatures.cend()) {
|
||||||
parseViews(node);
|
parseViews(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -384,10 +385,11 @@ void ThemeData::parseViews(const pugi::xml_node& root)
|
||||||
prevOff = nameAttr.find_first_not_of(delim, off);
|
prevOff = nameAttr.find_first_not_of(delim, off);
|
||||||
off = nameAttr.find_first_of(delim, prevOff);
|
off = nameAttr.find_first_of(delim, prevOff);
|
||||||
|
|
||||||
if (std::find(sSupportedViews.cbegin(), sSupportedViews.cend(),
|
if (std::find(sSupportedViews.cbegin(), sSupportedViews.cend(), viewKey) !=
|
||||||
viewKey) != sSupportedViews.cend()) {
|
sSupportedViews.cend()) {
|
||||||
ThemeView& view = mViews.insert(std::pair<std::string,
|
ThemeView& view =
|
||||||
ThemeView>(viewKey, ThemeView())).first->second;
|
mViews.insert(std::pair<std::string, ThemeView>(viewKey, ThemeView()))
|
||||||
|
.first->second;
|
||||||
parseView(node, view);
|
parseView(node, view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -416,19 +418,21 @@ void ThemeData::parseView(const pugi::xml_node& root, ThemeView& view)
|
||||||
prevOff = nameAttr.find_first_not_of(delim, off);
|
prevOff = nameAttr.find_first_not_of(delim, off);
|
||||||
off = nameAttr.find_first_of(delim, prevOff);
|
off = nameAttr.find_first_of(delim, prevOff);
|
||||||
|
|
||||||
parseElement(node, elemTypeIt->second,
|
parseElement(
|
||||||
view.elements.insert(std::pair<std::string,
|
node, elemTypeIt->second,
|
||||||
ThemeElement>(elemKey, ThemeElement())).first->second);
|
view.elements.insert(std::pair<std::string, ThemeElement>(elemKey, ThemeElement()))
|
||||||
|
.first->second);
|
||||||
|
|
||||||
if (std::find(view.orderedKeys.cbegin(), view.orderedKeys.cend(),
|
if (std::find(view.orderedKeys.cbegin(), view.orderedKeys.cend(), elemKey) ==
|
||||||
elemKey) == view.orderedKeys.cend())
|
view.orderedKeys.cend())
|
||||||
view.orderedKeys.push_back(elemKey);
|
view.orderedKeys.push_back(elemKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThemeData::parseElement(const pugi::xml_node& root,
|
void ThemeData::parseElement(const pugi::xml_node& root,
|
||||||
const std::map<std::string, ElementPropertyType>& typeMap, ThemeElement& element)
|
const std::map<std::string, ElementPropertyType>& typeMap,
|
||||||
|
ThemeElement& element)
|
||||||
{
|
{
|
||||||
ThemeException error;
|
ThemeException error;
|
||||||
error.setFiles(mPaths);
|
error.setFiles(mPaths);
|
||||||
|
@ -439,85 +443,88 @@ void ThemeData::parseElement(const pugi::xml_node& root,
|
||||||
for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) {
|
for (pugi::xml_node node = root.first_child(); node; node = node.next_sibling()) {
|
||||||
auto typeIt = typeMap.find(node.name());
|
auto typeIt = typeMap.find(node.name());
|
||||||
if (typeIt == typeMap.cend())
|
if (typeIt == typeMap.cend())
|
||||||
throw error << ": Unknown property type \"" << node.name() <<
|
throw error << ": Unknown property type \"" << node.name() << "\" (for element of type "
|
||||||
"\" (for element of type " << root.name() << ")";
|
<< root.name() << ")";
|
||||||
|
|
||||||
std::string str = resolvePlaceholders(node.text().as_string());
|
std::string str = resolvePlaceholders(node.text().as_string());
|
||||||
|
|
||||||
switch (typeIt->second) {
|
switch (typeIt->second) {
|
||||||
case NORMALIZED_RECT: {
|
case NORMALIZED_RECT: {
|
||||||
Vector4f val;
|
Vector4f val;
|
||||||
|
|
||||||
auto splits = Utils::String::delimitedStringToVector(str, " ");
|
auto splits = Utils::String::delimitedStringToVector(str, " ");
|
||||||
if (splits.size() == 2) {
|
if (splits.size() == 2) {
|
||||||
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
|
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
|
||||||
static_cast<float>(atof(splits.at(1).c_str())),
|
static_cast<float>(atof(splits.at(1).c_str())),
|
||||||
static_cast<float>(atof(splits.at(0).c_str())),
|
static_cast<float>(atof(splits.at(0).c_str())),
|
||||||
static_cast<float>(atof(splits.at(1).c_str())));
|
static_cast<float>(atof(splits.at(1).c_str())));
|
||||||
|
}
|
||||||
|
else if (splits.size() == 4) {
|
||||||
|
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
|
||||||
|
static_cast<float>(atof(splits.at(1).c_str())),
|
||||||
|
static_cast<float>(atof(splits.at(2).c_str())),
|
||||||
|
static_cast<float>(atof(splits.at(3).c_str())));
|
||||||
|
}
|
||||||
|
|
||||||
|
element.properties[node.name()] = val;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
else if (splits.size() == 4) {
|
case NORMALIZED_PAIR: {
|
||||||
val = Vector4f(static_cast<float>(atof(splits.at(0).c_str())),
|
size_t divider = str.find(' ');
|
||||||
static_cast<float>(atof(splits.at(1).c_str())),
|
if (divider == std::string::npos)
|
||||||
static_cast<float>(atof(splits.at(2).c_str())),
|
throw error << "invalid normalized pair (property \"" << node.name()
|
||||||
static_cast<float>(atof(splits.at(3).c_str())));
|
<< "\", value \"" << str.c_str() << "\")";
|
||||||
|
|
||||||
|
std::string first = str.substr(0, divider);
|
||||||
|
std::string second = str.substr(divider, std::string::npos);
|
||||||
|
|
||||||
|
Vector2f val(static_cast<float>(atof(first.c_str())),
|
||||||
|
static_cast<float>(atof(second.c_str())));
|
||||||
|
|
||||||
|
element.properties[node.name()] = val;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case STRING: {
|
||||||
element.properties[node.name()] = val;
|
element.properties[node.name()] = str;
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case NORMALIZED_PAIR: {
|
|
||||||
size_t divider = str.find(' ');
|
|
||||||
if (divider == std::string::npos)
|
|
||||||
throw error << "invalid normalized pair (property \"" << node.name() <<
|
|
||||||
"\", value \"" << str.c_str() << "\")";
|
|
||||||
|
|
||||||
std::string first = str.substr(0, divider);
|
|
||||||
std::string second = str.substr(divider, std::string::npos);
|
|
||||||
|
|
||||||
Vector2f val(static_cast<float>(atof(first.c_str())),
|
|
||||||
static_cast<float>(atof(second.c_str())));
|
|
||||||
|
|
||||||
element.properties[node.name()] = val;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case STRING: {
|
|
||||||
element.properties[node.name()] = str;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case PATH: {
|
|
||||||
std::string path = Utils::FileSystem::resolveRelativePath(str, mPaths.back(), true);
|
|
||||||
if (!ResourceManager::getInstance()->fileExists(path)) {
|
|
||||||
std::stringstream ss;
|
|
||||||
// "From theme yadda yadda, included file yadda yadda.
|
|
||||||
LOG(LogWarning) << error.msg << ":";
|
|
||||||
LOG(LogWarning) << "Could not find file \"" << node.text().get() << "\" " <<
|
|
||||||
((node.text().get() != path) ? "which resolves to \"" + path + "\"" : "");
|
|
||||||
}
|
}
|
||||||
element.properties[node.name()] = path;
|
case PATH: {
|
||||||
break;
|
std::string path = Utils::FileSystem::resolveRelativePath(str, mPaths.back(), true);
|
||||||
}
|
if (!ResourceManager::getInstance()->fileExists(path)) {
|
||||||
case COLOR: {
|
std::stringstream ss;
|
||||||
element.properties[node.name()] = getHexColor(str);
|
// "From theme yadda yadda, included file yadda yadda.
|
||||||
break;
|
LOG(LogWarning) << error.msg << ":";
|
||||||
}
|
LOG(LogWarning)
|
||||||
case FLOAT: {
|
<< "Could not find file \"" << node.text().get() << "\" "
|
||||||
float floatVal = static_cast<float>(strtod(str.c_str(), 0));
|
<< ((node.text().get() != path) ? "which resolves to \"" + path + "\"" :
|
||||||
element.properties[node.name()] = floatVal;
|
"");
|
||||||
break;
|
}
|
||||||
}
|
element.properties[node.name()] = path;
|
||||||
case BOOLEAN: {
|
break;
|
||||||
// Only look at first char.
|
}
|
||||||
char first = str[0];
|
case COLOR: {
|
||||||
// 1*, t* (true), T* (True), y* (yes), Y* (YES)
|
element.properties[node.name()] = getHexColor(str);
|
||||||
bool boolVal = (first == '1' || first == 't' || first == 'T' ||
|
break;
|
||||||
first == 'y' || first == 'Y');
|
}
|
||||||
|
case FLOAT: {
|
||||||
|
float floatVal = static_cast<float>(strtod(str.c_str(), 0));
|
||||||
|
element.properties[node.name()] = floatVal;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BOOLEAN: {
|
||||||
|
// Only look at first char.
|
||||||
|
char first = str[0];
|
||||||
|
// 1*, t* (true), T* (True), y* (yes), Y* (YES)
|
||||||
|
bool boolVal =
|
||||||
|
(first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y');
|
||||||
|
|
||||||
element.properties[node.name()] = boolVal;
|
element.properties[node.name()] = boolVal;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default: {
|
||||||
throw error << "Unknown ElementPropertyType for \"" <<
|
throw error << "Unknown ElementPropertyType for \""
|
||||||
root.attribute("name").as_string() << "\", property " << node.name();
|
<< root.attribute("name").as_string() << "\", property " << node.name();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -529,7 +536,8 @@ bool ThemeData::hasView(const std::string& view)
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThemeData::ThemeElement* ThemeData::getElement(const std::string& view,
|
const ThemeData::ThemeElement* ThemeData::getElement(const std::string& view,
|
||||||
const std::string& element, const std::string& expectedType) const
|
const std::string& element,
|
||||||
|
const std::string& expectedType) const
|
||||||
{
|
{
|
||||||
auto viewIt = mViews.find(view);
|
auto viewIt = mViews.find(view);
|
||||||
if (viewIt == mViews.cend())
|
if (viewIt == mViews.cend())
|
||||||
|
@ -540,9 +548,9 @@ const ThemeData::ThemeElement* ThemeData::getElement(const std::string& view,
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (elemIt->second.type != expectedType && !expectedType.empty()) {
|
if (elemIt->second.type != expectedType && !expectedType.empty()) {
|
||||||
LOG(LogWarning) << " requested mismatched theme type for [" <<
|
LOG(LogWarning) << " requested mismatched theme type for [" << view << "." << element
|
||||||
view << "." << element << "] - expected \""
|
<< "] - expected \"" << expectedType << "\", got \"" << elemIt->second.type
|
||||||
<< expectedType << "\", got \"" << elemIt->second.type << "\"";
|
<< "\"";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -555,14 +563,14 @@ const std::shared_ptr<ThemeData>& ThemeData::getDefault()
|
||||||
if (theme == nullptr) {
|
if (theme == nullptr) {
|
||||||
theme = std::shared_ptr<ThemeData>(new ThemeData());
|
theme = std::shared_ptr<ThemeData>(new ThemeData());
|
||||||
|
|
||||||
const std::string path = Utils::FileSystem::getHomePath() +
|
const std::string path =
|
||||||
"/.emulationstation/es_theme_default.xml";
|
Utils::FileSystem::getHomePath() + "/.emulationstation/es_theme_default.xml";
|
||||||
if (Utils::FileSystem::exists(path)) {
|
if (Utils::FileSystem::exists(path)) {
|
||||||
try {
|
try {
|
||||||
std::map<std::string, std::string> emptyMap;
|
std::map<std::string, std::string> emptyMap;
|
||||||
theme->loadFile(emptyMap, path);
|
theme->loadFile(emptyMap, path);
|
||||||
}
|
}
|
||||||
catch(ThemeException& e) {
|
catch (ThemeException& e) {
|
||||||
LOG(LogError) << e.what();
|
LOG(LogError) << e.what();
|
||||||
theme = std::shared_ptr<ThemeData>(new ThemeData()); // Reset to empty.
|
theme = std::shared_ptr<ThemeData>(new ThemeData()); // Reset to empty.
|
||||||
}
|
}
|
||||||
|
@ -573,7 +581,8 @@ const std::shared_ptr<ThemeData>& ThemeData::getDefault()
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<GuiComponent*> ThemeData::makeExtras(const std::shared_ptr<ThemeData>& theme,
|
std::vector<GuiComponent*> ThemeData::makeExtras(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, Window* window)
|
const std::string& view,
|
||||||
|
Window* window)
|
||||||
{
|
{
|
||||||
std::vector<GuiComponent*> comps;
|
std::vector<GuiComponent*> comps;
|
||||||
|
|
||||||
|
@ -581,8 +590,8 @@ std::vector<GuiComponent*> ThemeData::makeExtras(const std::shared_ptr<ThemeData
|
||||||
if (viewIt == theme->mViews.cend())
|
if (viewIt == theme->mViews.cend())
|
||||||
return comps;
|
return comps;
|
||||||
|
|
||||||
for (auto it = viewIt->second.orderedKeys.cbegin();
|
for (auto it = viewIt->second.orderedKeys.cbegin(); // Line break.
|
||||||
it != viewIt->second.orderedKeys.cend(); it++) {
|
it != viewIt->second.orderedKeys.cend(); it++) {
|
||||||
ThemeElement& elem = viewIt->second.elements.at(*it);
|
ThemeElement& elem = viewIt->second.elements.at(*it);
|
||||||
if (elem.extra) {
|
if (elem.extra) {
|
||||||
GuiComponent* comp = nullptr;
|
GuiComponent* comp = nullptr;
|
||||||
|
@ -608,19 +617,20 @@ std::map<std::string, ThemeSet> ThemeData::getThemeSets()
|
||||||
std::map<std::string, ThemeSet> sets;
|
std::map<std::string, ThemeSet> sets;
|
||||||
|
|
||||||
// Check for themes first under the home directory, then under the data installation
|
// Check for themes first under the home directory, then under the data installation
|
||||||
// directory (Unix only) and last under the ES executable directory.
|
// directory (Unix only) and last under the ES-DE binary directory.
|
||||||
#if defined(__unix__) || defined(__APPLE__)
|
|
||||||
|
#if defined(__unix__) || defined(__APPLE__)
|
||||||
static const size_t pathCount = 3;
|
static const size_t pathCount = 3;
|
||||||
#else
|
#else
|
||||||
static const size_t pathCount = 2;
|
static const size_t pathCount = 2;
|
||||||
#endif
|
#endif
|
||||||
std::string paths[pathCount] = {
|
std::string paths[pathCount] = {
|
||||||
Utils::FileSystem::getExePath() + "/themes",
|
Utils::FileSystem::getExePath() + "/themes",
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
Utils::FileSystem::getExePath() + "/../Resources/themes",
|
Utils::FileSystem::getExePath() + "/../Resources/themes",
|
||||||
#elif defined(__unix__)
|
#elif defined(__unix__)
|
||||||
Utils::FileSystem::getProgramDataPath() + "/themes",
|
Utils::FileSystem::getProgramDataPath() + "/themes",
|
||||||
#endif
|
#endif
|
||||||
Utils::FileSystem::getHomePath() + "/.emulationstation/themes"
|
Utils::FileSystem::getHomePath() + "/.emulationstation/themes"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -631,9 +641,9 @@ std::map<std::string, ThemeSet> ThemeData::getThemeSets()
|
||||||
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(paths[i]);
|
Utils::FileSystem::stringList dirContent = Utils::FileSystem::getDirContent(paths[i]);
|
||||||
|
|
||||||
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
|
for (Utils::FileSystem::stringList::const_iterator it = dirContent.cbegin();
|
||||||
it != dirContent.cend(); it++) {
|
it != dirContent.cend(); it++) {
|
||||||
if (Utils::FileSystem::isDirectory(*it)) {
|
if (Utils::FileSystem::isDirectory(*it)) {
|
||||||
ThemeSet set = {*it};
|
ThemeSet set = { *it };
|
||||||
sets[set.getName()] = set;
|
sets[set.getName()] = set;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,9 +660,9 @@ std::string ThemeData::getThemeFromCurrentSet(const std::string& system)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
std::map<std::string, ThemeSet>::const_iterator set =
|
std::map<std::string, ThemeSet>::const_iterator set =
|
||||||
themeSets.find(Settings::getInstance()->getString("ThemeSet"));
|
themeSets.find(Settings::getInstance()->getString("ThemeSet"));
|
||||||
if (set == themeSets.cend()) {
|
if (set == themeSets.cend()) {
|
||||||
// Currently selected theme set is missing, so just pick the first available set.
|
// Currently configured theme set is missing, so just pick the first available set.
|
||||||
set = themeSets.cbegin();
|
set = themeSets.cbegin();
|
||||||
Settings::getInstance()->setString("ThemeSet", set->first);
|
Settings::getInstance()->setString("ThemeSet", set->first);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,10 +21,12 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace pugi { class xml_node; }
|
namespace pugi
|
||||||
|
{
|
||||||
|
class xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> class TextListComponent;
|
||||||
class TextListComponent;
|
|
||||||
|
|
||||||
class GuiComponent;
|
class GuiComponent;
|
||||||
class ImageComponent;
|
class ImageComponent;
|
||||||
|
@ -63,10 +65,9 @@ public:
|
||||||
|
|
||||||
virtual const char* what() const throw() { return msg.c_str(); }
|
virtual const char* what() const throw() { return msg.c_str(); }
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> friend ThemeException& operator<<(ThemeException& e, T msg);
|
||||||
friend ThemeException& operator<<(ThemeException& e, T msg);
|
|
||||||
|
|
||||||
inline void setFiles(const std::deque<std::string>& deque)
|
void setFiles(const std::deque<std::string>& deque)
|
||||||
{
|
{
|
||||||
*this << "From theme \"" << deque.front() << "\"";
|
*this << "From theme \"" << deque.front() << "\"";
|
||||||
for (auto it = deque.cbegin() + 1; it != deque.cend(); it++)
|
for (auto it = deque.cbegin() + 1; it != deque.cend(); it++)
|
||||||
|
@ -74,8 +75,7 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> ThemeException& operator<<(ThemeException& e, T appendMsg)
|
||||||
ThemeException& operator<<(ThemeException& e, T appendMsg)
|
|
||||||
{
|
{
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << e.msg << appendMsg;
|
ss << e.msg << appendMsg;
|
||||||
|
@ -83,13 +83,14 @@ ThemeException& operator<<(ThemeException& e, T appendMsg)
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ThemeSet
|
struct ThemeSet {
|
||||||
{
|
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|
||||||
inline std::string getName() const { return Utils::FileSystem::getStem(path); }
|
std::string getName() const { return Utils::FileSystem::getStem(path); }
|
||||||
inline std::string getThemePath(const std::string& system) const
|
std::string getThemePath(const std::string& system) const
|
||||||
{ return path + "/" + system + "/theme.xml"; }
|
{
|
||||||
|
return path + "/" + system + "/theme.xml";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class ThemeData
|
class ThemeData
|
||||||
|
@ -124,8 +125,7 @@ public:
|
||||||
|
|
||||||
std::map<std::string, Property> properties;
|
std::map<std::string, Property> properties;
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> const T get(const std::string& prop) const
|
||||||
const T get(const std::string& prop) const
|
|
||||||
{
|
{
|
||||||
if (std::is_same<T, Vector2f>::value)
|
if (std::is_same<T, Vector2f>::value)
|
||||||
return *(const T*)&properties.at(prop).v;
|
return *(const T*)&properties.at(prop).v;
|
||||||
|
@ -142,8 +142,10 @@ public:
|
||||||
return T();
|
return T();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool has(const std::string& prop) const
|
bool has(const std::string& prop) const
|
||||||
{ return (properties.find(prop) != properties.cend()); }
|
{
|
||||||
|
return (properties.find(prop) != properties.cend());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -173,11 +175,13 @@ public:
|
||||||
bool hasView(const std::string& view);
|
bool hasView(const std::string& view);
|
||||||
|
|
||||||
// If expectedType is an empty string, will do no type checking.
|
// If expectedType is an empty string, will do no type checking.
|
||||||
const ThemeElement* getElement(const std::string& view, const std::string& element,
|
const ThemeElement* getElement(const std::string& view,
|
||||||
const std::string& expectedType) const;
|
const std::string& element,
|
||||||
|
const std::string& expectedType) const;
|
||||||
|
|
||||||
static std::vector<GuiComponent*> makeExtras(const std::shared_ptr<ThemeData>& theme,
|
static std::vector<GuiComponent*> makeExtras(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, Window* window);
|
const std::string& view,
|
||||||
|
Window* window);
|
||||||
|
|
||||||
static const std::shared_ptr<ThemeData>& getDefault();
|
static const std::shared_ptr<ThemeData>& getDefault();
|
||||||
|
|
||||||
|
@ -198,7 +202,8 @@ private:
|
||||||
void parseViews(const pugi::xml_node& themeRoot);
|
void parseViews(const pugi::xml_node& themeRoot);
|
||||||
void parseView(const pugi::xml_node& viewNode, ThemeView& view);
|
void parseView(const pugi::xml_node& viewNode, ThemeView& view);
|
||||||
void parseElement(const pugi::xml_node& elementNode,
|
void parseElement(const pugi::xml_node& elementNode,
|
||||||
const std::map<std::string, ElementPropertyType>& typeMap, ThemeElement& element);
|
const std::map<std::string, ElementPropertyType>& typeMap,
|
||||||
|
ThemeElement& element);
|
||||||
|
|
||||||
std::map<std::string, ThemeView> mViews;
|
std::map<std::string, ThemeView> mViews;
|
||||||
};
|
};
|
||||||
|
|
|
@ -14,40 +14,41 @@
|
||||||
#if defined(BUILD_VLC_PLAYER)
|
#if defined(BUILD_VLC_PLAYER)
|
||||||
#include "components/VideoVlcComponent.h"
|
#include "components/VideoVlcComponent.h"
|
||||||
#endif
|
#endif
|
||||||
#include "resources/Font.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "InputManager.h"
|
#include "InputManager.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Scripting.h"
|
|
||||||
#include "Sound.h"
|
#include "Sound.h"
|
||||||
|
#include "resources/Font.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
|
|
||||||
|
#define CLOCK_BACKGROUND_CREATION false
|
||||||
|
|
||||||
Window::Window()
|
Window::Window()
|
||||||
: mScreensaver(nullptr),
|
: mScreensaver(nullptr)
|
||||||
mMediaViewer(nullptr),
|
, mMediaViewer(nullptr)
|
||||||
mLaunchScreen(nullptr),
|
, mLaunchScreen(nullptr)
|
||||||
mInfoPopup(nullptr),
|
, mInfoPopup(nullptr)
|
||||||
mNormalizeNextUpdate(false),
|
, mNormalizeNextUpdate(false)
|
||||||
mFrameTimeElapsed(0),
|
, mFrameTimeElapsed(0)
|
||||||
mFrameCountElapsed(0),
|
, mFrameCountElapsed(0)
|
||||||
mAverageDeltaTime(10),
|
, mAverageDeltaTime(10)
|
||||||
mAllowSleep(true),
|
, mAllowSleep(true)
|
||||||
mSleeping(false),
|
, mSleeping(false)
|
||||||
mTimeSinceLastInput(0),
|
, mTimeSinceLastInput(0)
|
||||||
mRenderScreensaver(false),
|
, mRenderScreensaver(false)
|
||||||
mRenderMediaViewer(false),
|
, mRenderMediaViewer(false)
|
||||||
mRenderLaunchScreen(false),
|
, mRenderLaunchScreen(false)
|
||||||
mGameLaunchedState(false),
|
, mGameLaunchedState(false)
|
||||||
mAllowTextScrolling(true),
|
, mAllowTextScrolling(true)
|
||||||
mCachedBackground(false),
|
, mCachedBackground(false)
|
||||||
mInvalidatedCachedBackground(false),
|
, mInvalidatedCachedBackground(false)
|
||||||
mVideoPlayerCount(0),
|
, mVideoPlayerCount(0)
|
||||||
mTopOpacity(0),
|
, mTopOpacity(0)
|
||||||
mTopScale(0.5),
|
, mTopScale(0.5)
|
||||||
mListScrollOpacity(0),
|
, mListScrollOpacity(0)
|
||||||
mChangedThemeSet(false)
|
, mChangedThemeSet(false)
|
||||||
{
|
{
|
||||||
mHelp = new HelpComponent(this);
|
mHelp = new HelpComponent(this);
|
||||||
mBackgroundOverlay = new ImageComponent(this);
|
mBackgroundOverlay = new ImageComponent(this);
|
||||||
|
@ -122,7 +123,7 @@ bool Window::init()
|
||||||
|
|
||||||
mBackgroundOverlay->setImage(":/graphics/screen_gradient.png");
|
mBackgroundOverlay->setImage(":/graphics/screen_gradient.png");
|
||||||
mBackgroundOverlay->setResize(static_cast<float>(Renderer::getScreenWidth()),
|
mBackgroundOverlay->setResize(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
mListScrollFont = Font::get(FONT_SIZE_LARGE);
|
mListScrollFont = Font::get(FONT_SIZE_LARGE);
|
||||||
|
|
||||||
|
@ -141,9 +142,9 @@ void Window::deinit()
|
||||||
|
|
||||||
InputManager::getInstance()->deinit();
|
InputManager::getInstance()->deinit();
|
||||||
ResourceManager::getInstance()->unloadAll();
|
ResourceManager::getInstance()->unloadAll();
|
||||||
#if defined(BUILD_VLC_PLAYER)
|
#if defined(BUILD_VLC_PLAYER)
|
||||||
VideoVlcComponent::deinit();
|
VideoVlcComponent::deinit();
|
||||||
#endif
|
#endif
|
||||||
Renderer::deinit();
|
Renderer::deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,8 +155,9 @@ void Window::input(InputConfig* config, Input input)
|
||||||
// The DebugSkipInputLogging option has to be set manually in es_settings.xml as
|
// The DebugSkipInputLogging option has to be set manually in es_settings.xml as
|
||||||
// it does not have any settings menu entry.
|
// it does not have any settings menu entry.
|
||||||
if (Settings::getInstance()->getBool("Debug") &&
|
if (Settings::getInstance()->getBool("Debug") &&
|
||||||
!Settings::getInstance()->getBool("DebugSkipInputLogging"))
|
!Settings::getInstance()->getBool("DebugSkipInputLogging")) {
|
||||||
logInput(config, input);
|
logInput(config, input);
|
||||||
|
}
|
||||||
|
|
||||||
if (mMediaViewer && mRenderMediaViewer) {
|
if (mMediaViewer && mRenderMediaViewer) {
|
||||||
if (config->isMappedLike("right", input) && input.value != 0)
|
if (config->isMappedLike("right", input) && input.value != 0)
|
||||||
|
@ -177,17 +179,17 @@ void Window::input(InputConfig* config, Input input)
|
||||||
|
|
||||||
if (mScreensaver) {
|
if (mScreensaver) {
|
||||||
if (mScreensaver->isScreensaverActive() &&
|
if (mScreensaver->isScreensaverActive() &&
|
||||||
Settings::getInstance()->getBool("ScreensaverControls") &&
|
Settings::getInstance()->getBool("ScreensaverControls") &&
|
||||||
((Settings::getInstance()->getString("ScreensaverType") == "video") ||
|
((Settings::getInstance()->getString("ScreensaverType") == "video") ||
|
||||||
(Settings::getInstance()->getString("ScreensaverType") == "slideshow"))) {
|
(Settings::getInstance()->getString("ScreensaverType") == "slideshow"))) {
|
||||||
bool customImageSlideshow = false;
|
bool customImageSlideshow = false;
|
||||||
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow" &&
|
if (Settings::getInstance()->getString("ScreensaverType") == "slideshow" &&
|
||||||
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages"))
|
Settings::getInstance()->getBool("ScreensaverSlideshowCustomImages"))
|
||||||
customImageSlideshow = true;
|
customImageSlideshow = true;
|
||||||
|
|
||||||
if ((customImageSlideshow || mScreensaver->getCurrentGame() != nullptr) &&
|
if ((customImageSlideshow || mScreensaver->getCurrentGame() != nullptr) &&
|
||||||
(config->isMappedTo("a", input) || config->isMappedTo("y", input) ||
|
(config->isMappedTo("a", input) || config->isMappedTo("y", input) ||
|
||||||
config->isMappedLike("left", input) || config->isMappedLike("right", input))) {
|
config->isMappedLike("left", input) || config->isMappedLike("right", input))) {
|
||||||
// Left or right browses to the next video or image.
|
// Left or right browses to the next video or image.
|
||||||
if (config->isMappedLike("left", input) || config->isMappedLike("right", input)) {
|
if (config->isMappedLike("left", input) || config->isMappedLike("right", input)) {
|
||||||
if (input.value != 0) {
|
if (input.value != 0) {
|
||||||
|
@ -230,22 +232,22 @@ void Window::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_g &&
|
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_g &&
|
||||||
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
||||||
// Toggle debug grid with Ctrl-G.
|
// Toggle debug grid with Ctrl-G.
|
||||||
Settings::getInstance()->setBool("DebugGrid",
|
Settings::getInstance()->setBool("DebugGrid",
|
||||||
!Settings::getInstance()->getBool("DebugGrid"));
|
!Settings::getInstance()->getBool("DebugGrid"));
|
||||||
}
|
}
|
||||||
else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_t &&
|
else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_t &&
|
||||||
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
||||||
// Toggle TextComponent debug view with Ctrl-T.
|
// Toggle TextComponent debug view with Ctrl-T.
|
||||||
Settings::getInstance()->setBool("DebugText",
|
Settings::getInstance()->setBool("DebugText",
|
||||||
!Settings::getInstance()->getBool("DebugText"));
|
!Settings::getInstance()->getBool("DebugText"));
|
||||||
}
|
}
|
||||||
else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_i &&
|
else if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_i &&
|
||||||
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
|
||||||
// Toggle ImageComponent debug view with Ctrl-I.
|
// Toggle ImageComponent debug view with Ctrl-I.
|
||||||
Settings::getInstance()->setBool("DebugImage",
|
Settings::getInstance()->setBool("DebugImage",
|
||||||
!Settings::getInstance()->getBool("DebugImage"));
|
!Settings::getInstance()->getBool("DebugImage"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (peekGui())
|
if (peekGui())
|
||||||
|
@ -270,8 +272,8 @@ void Window::logInput(InputConfig* config, Input input)
|
||||||
mapname += ", ";
|
mapname += ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(LogDebug) << "Window::logInput(" << config->getDeviceName() << "): " <<
|
LOG(LogDebug) << "Window::logInput(" << config->getDeviceName() << "): " << input.string()
|
||||||
input.string() << ", isMappedTo=" << mapname << "value=" << input.value;
|
<< ", isMappedTo=" << mapname << "value=" << input.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::update(int deltaTime)
|
void Window::update(int deltaTime)
|
||||||
|
@ -292,12 +294,13 @@ void Window::update(int deltaTime)
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
|
||||||
// FPS.
|
// FPS.
|
||||||
ss << std::fixed << std::setprecision(1) <<
|
ss << std::fixed << std::setprecision(1)
|
||||||
(1000.0f * static_cast<float>(mFrameCountElapsed) /
|
<< (1000.0f * static_cast<float>(mFrameCountElapsed) /
|
||||||
static_cast<float>(mFrameTimeElapsed)) << " FPS (";
|
static_cast<float>(mFrameTimeElapsed))
|
||||||
ss << std::fixed << std::setprecision(2) <<
|
<< " FPS (";
|
||||||
(static_cast<float>(mFrameTimeElapsed) /
|
ss << std::fixed << std::setprecision(2)
|
||||||
static_cast<float>(mFrameCountElapsed)) << " ms)";
|
<< (static_cast<float>(mFrameTimeElapsed) / static_cast<float>(mFrameCountElapsed))
|
||||||
|
<< " ms)";
|
||||||
|
|
||||||
// The following calculations are not accurate, and the font calculation is completely
|
// The following calculations are not accurate, and the font calculation is completely
|
||||||
// broken. For now, still report the figures as it's somehow useful to locate memory
|
// broken. For now, still report the figures as it's somehow useful to locate memory
|
||||||
|
@ -307,12 +310,12 @@ void Window::update(int deltaTime)
|
||||||
float textureTotalUsageMiB = TextureResource::getTotalTextureSize() / 1024.0f / 1024.0f;
|
float textureTotalUsageMiB = TextureResource::getTotalTextureSize() / 1024.0f / 1024.0f;
|
||||||
float fontVramUsageMiB = Font::getTotalMemUsage() / 1024.0f / 1024.0f;
|
float fontVramUsageMiB = Font::getTotalMemUsage() / 1024.0f / 1024.0f;
|
||||||
|
|
||||||
ss << "\nFont VRAM: " << fontVramUsageMiB << " MiB\nTexture VRAM: " <<
|
ss << "\nFont VRAM: " << fontVramUsageMiB
|
||||||
textureVramUsageMiB << " MiB\nMax Texture VRAM: " <<
|
<< " MiB\nTexture VRAM: " << textureVramUsageMiB
|
||||||
textureTotalUsageMiB << " MiB";
|
<< " MiB\nMax Texture VRAM: " << textureTotalUsageMiB << " MiB";
|
||||||
mFrameDataText = std::unique_ptr<TextCache>
|
mFrameDataText = std::unique_ptr<TextCache>(mDefaultFonts.at(0)->buildTextCache(
|
||||||
(mDefaultFonts.at(0)->buildTextCache(ss.str(), Renderer::getScreenWidth() *
|
ss.str(), Renderer::getScreenWidth() * 0.02f, Renderer::getScreenHeight() * 0.02f,
|
||||||
0.02f, Renderer::getScreenHeight() * 0.02f, 0xFF00FFFF, 1.3f));
|
0xFF00FFFF, 1.3f));
|
||||||
}
|
}
|
||||||
|
|
||||||
mFrameTimeElapsed = 0;
|
mFrameTimeElapsed = 0;
|
||||||
|
@ -368,27 +371,29 @@ void Window::render()
|
||||||
else if (mRenderScreensaver && mScreensaver->isFallbackScreensaver())
|
else if (mRenderScreensaver && mScreensaver->isFallbackScreensaver())
|
||||||
renderBottom = true;
|
renderBottom = true;
|
||||||
else if (mRenderScreensaver &&
|
else if (mRenderScreensaver &&
|
||||||
Settings::getInstance()->getString("ScreensaverType") == "video")
|
Settings::getInstance()->getString("ScreensaverType") == "video")
|
||||||
renderBottom = false;
|
renderBottom = false;
|
||||||
else if (mRenderScreensaver &&
|
else if (mRenderScreensaver &&
|
||||||
Settings::getInstance()->getString("ScreensaverType") == "slideshow")
|
Settings::getInstance()->getString("ScreensaverType") == "slideshow")
|
||||||
renderBottom = false;
|
renderBottom = false;
|
||||||
|
|
||||||
if (renderBottom)
|
if (renderBottom)
|
||||||
bottom->render(transform);
|
bottom->render(transform);
|
||||||
|
|
||||||
if (bottom != top || mRenderLaunchScreen) {
|
if (bottom != top || mRenderLaunchScreen) {
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
if (!mCachedBackground) {
|
if (!mCachedBackground) {
|
||||||
// Generate a cache texture of the shaded background when opening the menu, which
|
// Generate a cache texture of the shaded background when opening the menu, which
|
||||||
// will remain valid until the menu is closed. This is way faster than having to
|
// will remain valid until the menu is closed. This is way faster than having to
|
||||||
// render the shaders for every frame.
|
// render the shaders for every frame.
|
||||||
// const auto backgroundStartTime = std::chrono::system_clock::now();
|
#if (CLOCK_BACKGROUND_CREATION)
|
||||||
|
const auto backgroundStartTime = std::chrono::system_clock::now();
|
||||||
|
#endif
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> mPostprocessedBackground;
|
std::shared_ptr<TextureResource> mPostprocessedBackground;
|
||||||
mPostprocessedBackground = TextureResource::get("");
|
mPostprocessedBackground = TextureResource::get("");
|
||||||
unsigned char* processedTexture = new unsigned char[Renderer::getScreenWidth() *
|
unsigned char* processedTexture =
|
||||||
Renderer::getScreenHeight() * 4];
|
new unsigned char[Renderer::getScreenWidth() * Renderer::getScreenHeight() * 4];
|
||||||
|
|
||||||
// Defocus the background using multiple passes of gaussian blur, with the number
|
// Defocus the background using multiple passes of gaussian blur, with the number
|
||||||
// of iterations relative to the screen resolution.
|
// of iterations relative to the screen resolution.
|
||||||
|
@ -396,7 +401,7 @@ void Window::render()
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("MenuBlurBackground")) {
|
if (Settings::getInstance()->getBool("MenuBlurBackground")) {
|
||||||
float heightModifier = Renderer::getScreenHeightModifier();
|
float heightModifier = Renderer::getScreenHeightModifier();
|
||||||
|
// clang-format off
|
||||||
if (heightModifier < 1)
|
if (heightModifier < 1)
|
||||||
backgroundParameters.blurPasses = 2; // Below 1080
|
backgroundParameters.blurPasses = 2; // Below 1080
|
||||||
else if (heightModifier >= 4)
|
else if (heightModifier >= 4)
|
||||||
|
@ -411,23 +416,25 @@ void Window::render()
|
||||||
backgroundParameters.blurPasses = 3; // 1440
|
backgroundParameters.blurPasses = 3; // 1440
|
||||||
else if (heightModifier >= 1)
|
else if (heightModifier >= 1)
|
||||||
backgroundParameters.blurPasses = 2; // 1080
|
backgroundParameters.blurPasses = 2; // 1080
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Also dim the background slightly.
|
// Also dim the background slightly.
|
||||||
backgroundParameters.fragmentDimValue = 0.60f;
|
backgroundParameters.fragmentDimValue = 0.60f;
|
||||||
|
|
||||||
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
|
Renderer::shaderPostprocessing(Renderer::SHADER_BLUR_HORIZONTAL |
|
||||||
Renderer::SHADER_BLUR_VERTICAL | Renderer::SHADER_DIM,
|
Renderer::SHADER_BLUR_VERTICAL |
|
||||||
backgroundParameters, processedTexture);
|
Renderer::SHADER_DIM,
|
||||||
|
backgroundParameters, processedTexture);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Dim the background slightly.
|
// Dim the background slightly.
|
||||||
backgroundParameters.fragmentDimValue = 0.60f;
|
backgroundParameters.fragmentDimValue = 0.60f;
|
||||||
Renderer::shaderPostprocessing(
|
Renderer::shaderPostprocessing(Renderer::SHADER_DIM, backgroundParameters,
|
||||||
Renderer::SHADER_DIM, backgroundParameters, processedTexture);
|
processedTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
mPostprocessedBackground->initFromPixels(processedTexture,
|
mPostprocessedBackground->initFromPixels(
|
||||||
Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
processedTexture, Renderer::getScreenWidth(), Renderer::getScreenHeight());
|
||||||
|
|
||||||
mBackgroundOverlay->setImage(mPostprocessedBackground);
|
mBackgroundOverlay->setImage(mPostprocessedBackground);
|
||||||
|
|
||||||
|
@ -444,10 +451,14 @@ void Window::render()
|
||||||
delete[] processedTexture;
|
delete[] processedTexture;
|
||||||
mCachedBackground = true;
|
mCachedBackground = true;
|
||||||
|
|
||||||
// const auto backgroundEndTime = std::chrono::system_clock::now();
|
#if (CLOCK_BACKGROUND_CREATION)
|
||||||
// LOG(LogDebug) << "Window::render(): Time to create cached background: " <<
|
const auto backgroundEndTime = std::chrono::system_clock::now();
|
||||||
// std::chrono::duration_cast<std::chrono::milliseconds>
|
LOG(LogDebug) << "Window::render(): Time to create cached background: "
|
||||||
// (backgroundEndTime - backgroundStartTime).count() << " ms";
|
<< std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||||
|
backgroundEndTime - backgroundStartTime)
|
||||||
|
.count()
|
||||||
|
<< " ms";
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
// Fade in the cached background if the menu opening effect has been set to scale-up.
|
// Fade in the cached background if the menu opening effect has been set to scale-up.
|
||||||
if (Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up") {
|
if (Settings::getInstance()->getString("MenuOpeningEffect") == "scale-up") {
|
||||||
|
@ -455,7 +466,7 @@ void Window::render()
|
||||||
if (mBackgroundOverlayOpacity < 255)
|
if (mBackgroundOverlayOpacity < 255)
|
||||||
mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
|
mBackgroundOverlayOpacity = Math::clamp(mBackgroundOverlayOpacity + 30, 0, 255);
|
||||||
}
|
}
|
||||||
#endif
|
#endif // USE_OPENGL_21
|
||||||
|
|
||||||
mBackgroundOverlay->render(transform);
|
mBackgroundOverlay->render(transform);
|
||||||
|
|
||||||
|
@ -464,8 +475,8 @@ void Window::render()
|
||||||
if (mTopScale < 1.0f) {
|
if (mTopScale < 1.0f) {
|
||||||
mTopScale = Math::clamp(mTopScale + 0.07f, 0.0f, 1.0f);
|
mTopScale = Math::clamp(mTopScale + 0.07f, 0.0f, 1.0f);
|
||||||
Vector2f topCenter = top->getCenter();
|
Vector2f topCenter = top->getCenter();
|
||||||
top->setOrigin({0.5, 0.5});
|
top->setOrigin({ 0.5f, 0.5f });
|
||||||
top->setPosition({topCenter.x(), topCenter.y(), 0});
|
top->setPosition({ topCenter.x(), topCenter.y(), 0.0f });
|
||||||
top->setScale(mTopScale);
|
top->setScale(mTopScale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -476,7 +487,7 @@ void Window::render()
|
||||||
else {
|
else {
|
||||||
mCachedBackground = false;
|
mCachedBackground = false;
|
||||||
mTopOpacity = 0;
|
mTopOpacity = 0;
|
||||||
mTopScale = 0.5;
|
mTopScale = 0.5f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -484,15 +495,15 @@ void Window::render()
|
||||||
if (mListScrollOpacity != 0) {
|
if (mListScrollOpacity != 0) {
|
||||||
Renderer::setMatrix(Transform4x4f::Identity());
|
Renderer::setMatrix(Transform4x4f::Identity());
|
||||||
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()),
|
static_cast<float>(Renderer::getScreenHeight()),
|
||||||
0x00000000 | mListScrollOpacity, 0x00000000 | mListScrollOpacity);
|
0x00000000 | mListScrollOpacity, 0x00000000 | mListScrollOpacity);
|
||||||
|
|
||||||
Vector2f offset = mListScrollFont->sizeText(mListScrollText);
|
Vector2f offset = mListScrollFont->sizeText(mListScrollText);
|
||||||
offset[0] = (Renderer::getScreenWidth() - offset.x()) * 0.5f;
|
offset[0] = (Renderer::getScreenWidth() - offset.x()) * 0.5f;
|
||||||
offset[1] = (Renderer::getScreenHeight() - offset.y()) * 0.5f;
|
offset[1] = (Renderer::getScreenHeight() - offset.y()) * 0.5f;
|
||||||
|
|
||||||
TextCache* cache = mListScrollFont->buildTextCache(mListScrollText,
|
TextCache* cache = mListScrollFont->buildTextCache(mListScrollText, offset.x(), offset.y(),
|
||||||
offset.x(), offset.y(), 0xFFFFFF00 | mListScrollOpacity);
|
0xFFFFFF00 | mListScrollOpacity);
|
||||||
mListScrollFont->renderTextCache(cache);
|
mListScrollFont->renderTextCache(cache);
|
||||||
delete cache;
|
delete cache;
|
||||||
}
|
}
|
||||||
|
@ -501,7 +512,7 @@ void Window::render()
|
||||||
mHelp->render(transform);
|
mHelp->render(transform);
|
||||||
|
|
||||||
unsigned int screensaverTimer =
|
unsigned int screensaverTimer =
|
||||||
static_cast<unsigned int>(Settings::getInstance()->getInt("ScreensaverTimer"));
|
static_cast<unsigned int>(Settings::getInstance()->getInt("ScreensaverTimer"));
|
||||||
if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) {
|
if (mTimeSinceLastInput >= screensaverTimer && screensaverTimer != 0) {
|
||||||
// If the media viewer is running or if a menu is open, reset the screensaver timer so
|
// If the media viewer is running or if a menu is open, reset the screensaver timer so
|
||||||
// that the screensaver won't start.
|
// that the screensaver won't start.
|
||||||
|
@ -549,17 +560,17 @@ void Window::renderLoadingScreen(std::string text)
|
||||||
Transform4x4f trans = Transform4x4f::Identity();
|
Transform4x4f trans = Transform4x4f::Identity();
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
||||||
|
|
||||||
ImageComponent splash(this, true);
|
ImageComponent splash(this, true);
|
||||||
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
splash.setResize(Renderer::getScreenWidth() * 0.6f, 0.0f);
|
||||||
splash.setImage(":/graphics/splash.svg");
|
splash.setImage(":/graphics/splash.svg");
|
||||||
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2,
|
splash.setPosition((Renderer::getScreenWidth() - splash.getSize().x()) / 2.0f,
|
||||||
(Renderer::getScreenHeight() - splash.getSize().y()) / 2 * 0.6f);
|
(Renderer::getScreenHeight() - splash.getSize().y()) / 2.0f * 0.6f);
|
||||||
splash.render(trans);
|
splash.render(trans);
|
||||||
|
|
||||||
auto& font = mDefaultFonts.at(1);
|
auto& font = mDefaultFonts.at(1);
|
||||||
TextCache* cache = font->buildTextCache(text, 0, 0, 0x656565FF);
|
TextCache* cache = font->buildTextCache(text, 0.0f, 0.0f, 0x656565FF);
|
||||||
|
|
||||||
float x = std::round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f);
|
float x = std::round((Renderer::getScreenWidth() - cache->metrics.size.x()) / 2.0f);
|
||||||
float y = std::round(Renderer::getScreenHeight() * 0.835f);
|
float y = std::round(Renderer::getScreenHeight() * 0.835f);
|
||||||
|
@ -602,20 +613,17 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
|
||||||
|
|
||||||
// Can we combine? (dpad only).
|
// Can we combine? (dpad only).
|
||||||
if ((it->first == "up/down" &&
|
if ((it->first == "up/down" &&
|
||||||
addPrompts.at(mappedTo->second).first != "left/right") ||
|
addPrompts.at(mappedTo->second).first != "left/right") ||
|
||||||
(it->first == "left/right" &&
|
(it->first == "left/right" &&
|
||||||
addPrompts.at(mappedTo->second).first != "up/down")) {
|
addPrompts.at(mappedTo->second).first != "up/down")) {
|
||||||
// Yes!
|
// Yes.
|
||||||
addPrompts.at(mappedTo->second).first = "up/down/left/right";
|
addPrompts.at(mappedTo->second).first = "up/down/left/right";
|
||||||
// Don't need to add this to addPrompts since we just merged.
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No, we can't combine!
|
|
||||||
addPrompts.push_back(*it);
|
addPrompts.push_back(*it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// No, it hasn't!
|
|
||||||
mappedToSeenMap.emplace(it->second, static_cast<int>(addPrompts.size()));
|
mappedToSeenMap.emplace(it->second, static_cast<int>(addPrompts.size()));
|
||||||
addPrompts.push_back(*it);
|
addPrompts.push_back(*it);
|
||||||
}
|
}
|
||||||
|
@ -624,29 +632,31 @@ void Window::setHelpPrompts(const std::vector<HelpPrompt>& prompts, const HelpSt
|
||||||
|
|
||||||
// Sort prompts so it goes [dpad_all] [dpad_u/d] [dpad_l/r] [a/b/x/y/l/r] [start/back].
|
// Sort prompts so it goes [dpad_all] [dpad_u/d] [dpad_l/r] [a/b/x/y/l/r] [start/back].
|
||||||
std::sort(addPrompts.begin(), addPrompts.end(),
|
std::sort(addPrompts.begin(), addPrompts.end(),
|
||||||
[](const HelpPrompt& a, const HelpPrompt& b) -> bool {
|
[](const HelpPrompt& a, const HelpPrompt& b) -> bool {
|
||||||
|
static const std::vector<std::string> map = { "up/down/left/right",
|
||||||
|
"up/down",
|
||||||
|
"left/right",
|
||||||
|
"a",
|
||||||
|
"b",
|
||||||
|
"x",
|
||||||
|
"y",
|
||||||
|
"l",
|
||||||
|
"r",
|
||||||
|
"start",
|
||||||
|
"back" };
|
||||||
|
int i = 0;
|
||||||
|
int aVal = 0;
|
||||||
|
int bVal = 0;
|
||||||
|
while (i < map.size()) {
|
||||||
|
if (a.first == map[i])
|
||||||
|
aVal = i;
|
||||||
|
if (b.first == map[i])
|
||||||
|
bVal = i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
static const std::vector<std::string> map = {
|
return aVal > bVal;
|
||||||
"up/down/left/right",
|
});
|
||||||
"up/down",
|
|
||||||
"left/right",
|
|
||||||
"a", "b", "x", "y", "l", "r",
|
|
||||||
"start", "back"
|
|
||||||
};
|
|
||||||
|
|
||||||
int i = 0;
|
|
||||||
int aVal = 0;
|
|
||||||
int bVal = 0;
|
|
||||||
while (i < map.size()) {
|
|
||||||
if (a.first == map[i])
|
|
||||||
aVal = i;
|
|
||||||
if (b.first == map[i])
|
|
||||||
bVal = i;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return aVal > bVal;
|
|
||||||
});
|
|
||||||
|
|
||||||
mHelp->setPrompts(addPrompts);
|
mHelp->setPrompts(addPrompts);
|
||||||
}
|
}
|
||||||
|
@ -789,18 +799,8 @@ void Window::invalidateCachedBackground()
|
||||||
mInvalidatedCachedBackground = true;
|
mInvalidatedCachedBackground = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Window::onSleep()
|
|
||||||
{
|
|
||||||
Scripting::fireEvent("sleep");
|
|
||||||
}
|
|
||||||
|
|
||||||
void Window::onWake()
|
|
||||||
{
|
|
||||||
Scripting::fireEvent("wake");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Window::isProcessing()
|
bool Window::isProcessing()
|
||||||
{
|
{
|
||||||
return count_if (mGuiStack.cbegin(), mGuiStack.cend(), [](GuiComponent* c) {
|
return count_if(mGuiStack.cbegin(), mGuiStack.cend(),
|
||||||
return c->isProcessing(); }) > 0;
|
[](GuiComponent* c) { return c->isProcessing(); }) > 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
#ifndef ES_CORE_WINDOW_H
|
#ifndef ES_CORE_WINDOW_H
|
||||||
#define ES_CORE_WINDOW_H
|
#define ES_CORE_WINDOW_H
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "HelpPrompt.h"
|
#include "HelpPrompt.h"
|
||||||
#include "InputConfig.h"
|
#include "InputConfig.h"
|
||||||
|
#include "Scripting.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
@ -78,7 +79,7 @@ public:
|
||||||
public:
|
public:
|
||||||
virtual void render(const Transform4x4f& parentTrans) = 0;
|
virtual void render(const Transform4x4f& parentTrans) = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
virtual ~InfoPopup() {};
|
virtual ~InfoPopup() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Window();
|
Window();
|
||||||
|
@ -87,7 +88,7 @@ public:
|
||||||
void pushGui(GuiComponent* gui);
|
void pushGui(GuiComponent* gui);
|
||||||
void removeGui(GuiComponent* gui);
|
void removeGui(GuiComponent* gui);
|
||||||
GuiComponent* peekGui();
|
GuiComponent* peekGui();
|
||||||
inline int getGuiStackSize() { return static_cast<int>(mGuiStack.size()); }
|
int getGuiStackSize() { return static_cast<int>(mGuiStack.size()); }
|
||||||
|
|
||||||
bool init();
|
bool init();
|
||||||
void deinit();
|
void deinit();
|
||||||
|
@ -102,7 +103,7 @@ public:
|
||||||
|
|
||||||
bool getAllowSleep() { return mAllowSleep; }
|
bool getAllowSleep() { return mAllowSleep; }
|
||||||
void setAllowSleep(bool sleep) { mAllowSleep = sleep; }
|
void setAllowSleep(bool sleep) { mAllowSleep = sleep; }
|
||||||
inline bool isSleeping() const { return mSleeping; }
|
bool isSleeping() const { return mSleeping; }
|
||||||
|
|
||||||
void renderLoadingScreen(std::string text);
|
void renderLoadingScreen(std::string text);
|
||||||
// The list scroll overlay is triggered from IList when the highest scrolling tier is reached.
|
// The list scroll overlay is triggered from IList when the highest scrolling tier is reached.
|
||||||
|
@ -148,8 +149,8 @@ public:
|
||||||
bool getChangedThemeSet() { return mChangedThemeSet; }
|
bool getChangedThemeSet() { return mChangedThemeSet; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onSleep();
|
void onSleep() { Scripting::fireEvent("sleep"); }
|
||||||
void onWake();
|
void onWake() { Scripting::fireEvent("wake"); }
|
||||||
|
|
||||||
// Returns true if at least one component on the stack is processing.
|
// Returns true if at least one component on the stack is processing.
|
||||||
bool isProcessing();
|
bool isProcessing();
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
class Animation
|
class Animation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~Animation() {};
|
virtual ~Animation() {}
|
||||||
virtual int getDuration() const = 0;
|
virtual int getDuration() const = 0;
|
||||||
virtual void apply(float t) = 0;
|
virtual void apply(float t) = 0;
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,16 +10,15 @@
|
||||||
|
|
||||||
#include "animations/Animation.h"
|
#include "animations/Animation.h"
|
||||||
|
|
||||||
AnimationController::AnimationController(
|
AnimationController::AnimationController(Animation* anim,
|
||||||
Animation* anim,
|
int delay,
|
||||||
int delay,
|
std::function<void()> finishedCallback,
|
||||||
std::function<void()> finishedCallback,
|
bool reverse)
|
||||||
bool reverse)
|
: mAnimation(anim)
|
||||||
: mAnimation(anim),
|
, mFinishedCallback(finishedCallback)
|
||||||
mFinishedCallback(finishedCallback),
|
, mReverse(reverse)
|
||||||
mReverse(reverse),
|
, mTime(-delay)
|
||||||
mTime(-delay),
|
, mDelay(delay)
|
||||||
mDelay(delay)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,20 +17,22 @@ class AnimationController
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Takes ownership of anim (will delete in destructor).
|
// Takes ownership of anim (will delete in destructor).
|
||||||
AnimationController(Animation* anim, int delay = 0,
|
AnimationController(Animation* anim,
|
||||||
std::function<void()> finishedCallback = nullptr, bool reverse = false);
|
int delay = 0,
|
||||||
|
std::function<void()> finishedCallback = nullptr,
|
||||||
|
bool reverse = false);
|
||||||
virtual ~AnimationController();
|
virtual ~AnimationController();
|
||||||
|
|
||||||
// Returns true if the animation is complete.
|
// Returns true if the animation is complete.
|
||||||
bool update(int deltaTime);
|
bool update(int deltaTime);
|
||||||
|
|
||||||
inline bool isReversed() const { return mReverse; }
|
bool isReversed() const { return mReverse; }
|
||||||
inline int getTime() const { return mTime; }
|
int getTime() const { return mTime; }
|
||||||
inline int getDelay() const { return mDelay; }
|
int getDelay() const { return mDelay; }
|
||||||
inline const std::function<void()>& getFinishedCallback() const { return mFinishedCallback; }
|
const std::function<void()>& getFinishedCallback() const { return mFinishedCallback; }
|
||||||
inline Animation* getAnimation() const { return mAnimation; }
|
Animation* getAnimation() const { return mAnimation; }
|
||||||
|
|
||||||
inline void removeFinishedCallback() { mFinishedCallback = nullptr; }
|
void removeFinishedCallback() { mFinishedCallback = nullptr; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Animation* mAnimation;
|
Animation* mAnimation;
|
||||||
|
|
|
@ -19,16 +19,15 @@ class LambdaAnimation : public Animation
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LambdaAnimation(const std::function<void(float t)>& func, int duration)
|
LambdaAnimation(const std::function<void(float t)>& func, int duration)
|
||||||
: mFunction(func), mDuration(duration) {}
|
: mFunction(func)
|
||||||
|
, mDuration(duration)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
virtual ~LambdaAnimation() = default;
|
virtual ~LambdaAnimation() = default;
|
||||||
|
|
||||||
int getDuration() const override { return mDuration; }
|
int getDuration() const override { return mDuration; }
|
||||||
|
void apply(float t) override { mFunction(t); }
|
||||||
void apply(float t) override
|
|
||||||
{
|
|
||||||
mFunction(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::function<void(float t)> mFunction;
|
std::function<void(float t)> mFunction;
|
||||||
|
|
|
@ -8,12 +8,13 @@
|
||||||
|
|
||||||
#include "components/AnimatedImageComponent.h"
|
#include "components/AnimatedImageComponent.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
AnimatedImageComponent::AnimatedImageComponent(Window* window)
|
AnimatedImageComponent::AnimatedImageComponent(Window* window)
|
||||||
: GuiComponent(window), mEnabled(false)
|
: GuiComponent(window)
|
||||||
|
, mEnabled(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,9 +26,9 @@ void AnimatedImageComponent::load(const AnimationDef* def)
|
||||||
|
|
||||||
for (size_t i = 0; i < def->frameCount; i++) {
|
for (size_t i = 0; i < def->frameCount; i++) {
|
||||||
if (def->frames[i].path != "" &&
|
if (def->frames[i].path != "" &&
|
||||||
!ResourceManager::getInstance()->fileExists(def->frames[i].path)) {
|
!ResourceManager::getInstance()->fileExists(def->frames[i].path)) {
|
||||||
LOG(LogError) << "Missing animation frame " << i <<
|
LOG(LogError) << "Missing animation frame " << i << " (\"" << def->frames[i].path
|
||||||
" (\"" << def->frames[i].path << "\")";
|
<< "\")";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,21 +14,23 @@
|
||||||
|
|
||||||
// Animation definition.
|
// Animation definition.
|
||||||
AnimationFrame BUSY_ANIMATION_FRAMES[] = {
|
AnimationFrame BUSY_ANIMATION_FRAMES[] = {
|
||||||
{":/graphics/busy_0.svg", 300},
|
{ ":/graphics/busy_0.svg", 300 },
|
||||||
{":/graphics/busy_1.svg", 300},
|
{ ":/graphics/busy_1.svg", 300 },
|
||||||
{":/graphics/busy_2.svg", 300},
|
{ ":/graphics/busy_2.svg", 300 },
|
||||||
{":/graphics/busy_3.svg", 300},
|
{ ":/graphics/busy_3.svg", 300 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const AnimationDef BUSY_ANIMATION_DEF = { BUSY_ANIMATION_FRAMES, 4, true };
|
const AnimationDef BUSY_ANIMATION_DEF = { BUSY_ANIMATION_FRAMES, 4, true };
|
||||||
|
|
||||||
BusyComponent::BusyComponent(Window* window): GuiComponent(window),
|
BusyComponent::BusyComponent(Window* window)
|
||||||
mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(5, 3))
|
: GuiComponent(window)
|
||||||
|
, mBackground(window, ":/graphics/frame.png")
|
||||||
|
, mGrid(window, Vector2i(5, 3))
|
||||||
{
|
{
|
||||||
mAnimation = std::make_shared<AnimatedImageComponent>(mWindow);
|
mAnimation = std::make_shared<AnimatedImageComponent>(mWindow);
|
||||||
mAnimation->load(&BUSY_ANIMATION_DEF);
|
mAnimation->load(&BUSY_ANIMATION_DEF);
|
||||||
mText = std::make_shared<TextComponent>(mWindow, "WORKING...",
|
mText = std::make_shared<TextComponent>(mWindow, "WORKING...", Font::get(FONT_SIZE_MEDIUM),
|
||||||
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
0x777777FF);
|
||||||
|
|
||||||
// Col 0 = animation, col 1 = spacer, col 2 = text.
|
// Col 0 = animation, col 1 = spacer, col 2 = text.
|
||||||
mGrid.setEntry(mAnimation, Vector2i(1, 1), false, true);
|
mGrid.setEntry(mAnimation, Vector2i(1, 1), false, true);
|
||||||
|
@ -57,13 +59,13 @@ void BusyComponent::onSizeChanged()
|
||||||
mGrid.setRowHeightPerc(1, textHeight / mSize.y());
|
mGrid.setRowHeightPerc(1, textHeight / mSize.y());
|
||||||
|
|
||||||
mBackground.setCornerSize({ 16.0f * Renderer::getScreenWidthModifier(),
|
mBackground.setCornerSize({ 16.0f * Renderer::getScreenWidthModifier(),
|
||||||
16.0f * Renderer::getScreenHeightModifier() });
|
16.0f * Renderer::getScreenHeightModifier() });
|
||||||
mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) +
|
mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) + mGrid.getColWidth(3),
|
||||||
mGrid.getColWidth(3), textHeight + (2 * Renderer::getScreenHeightModifier())),
|
textHeight + (2.0f * Renderer::getScreenHeightModifier())),
|
||||||
mAnimation->getPosition(), Vector2f(0, 0));
|
mAnimation->getPosition(), Vector2f(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusyComponent::reset()
|
void BusyComponent::reset()
|
||||||
{
|
{
|
||||||
//mAnimation->reset();
|
// mAnimation->reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
#define ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class AnimatedImageComponent;
|
class AnimatedImageComponent;
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
|
|
|
@ -8,20 +8,21 @@
|
||||||
|
|
||||||
#include "components/ButtonComponent.h"
|
#include "components/ButtonComponent.h"
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Settings.h"
|
|
||||||
|
|
||||||
ButtonComponent::ButtonComponent(
|
ButtonComponent::ButtonComponent(Window* window,
|
||||||
Window* window, const std::string& text,
|
const std::string& text,
|
||||||
const std::string& helpText,
|
const std::string& helpText,
|
||||||
const std::function<void()>& func)
|
const std::function<void()>& func)
|
||||||
: GuiComponent(window),
|
: GuiComponent(window)
|
||||||
mBox(window, ":/graphics/button.svg"),
|
, mBox(window, ":/graphics/button.svg")
|
||||||
mFont(Font::get(FONT_SIZE_MEDIUM)),
|
, mFont(Font::get(FONT_SIZE_MEDIUM))
|
||||||
mFocused(false),
|
, mFocused(false)
|
||||||
mEnabled(true),
|
, mEnabled(true)
|
||||||
mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF)
|
, mTextColorFocused(0xFFFFFFFF)
|
||||||
|
, mTextColorUnfocused(0x777777FF)
|
||||||
{
|
{
|
||||||
setPressedFunc(func);
|
setPressedFunc(func);
|
||||||
setText(text, helpText);
|
setText(text, helpText);
|
||||||
|
@ -30,12 +31,8 @@ ButtonComponent::ButtonComponent(
|
||||||
|
|
||||||
void ButtonComponent::onSizeChanged()
|
void ButtonComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
// Fit to mBox.
|
||||||
}
|
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
|
||||||
void ButtonComponent::setPressedFunc(std::function<void()> f)
|
|
||||||
{
|
|
||||||
mPressedFunc = f;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ButtonComponent::input(InputConfig* config, Input input)
|
bool ButtonComponent::input(InputConfig* config, Input input)
|
||||||
|
@ -56,9 +53,10 @@ void ButtonComponent::setText(const std::string& text, const std::string& helpTe
|
||||||
|
|
||||||
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(mText, 0, 0, getCurTextColor()));
|
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(mText, 0, 0, getCurTextColor()));
|
||||||
|
|
||||||
float minWidth = mFont->sizeText("DELETE").x() + (12 * Renderer::getScreenWidthModifier());
|
float minWidth = mFont->sizeText("DELETE").x() + (12.0f * Renderer::getScreenWidthModifier());
|
||||||
setSize(std::max(mTextCache->metrics.size.x() + (12 * Renderer::getScreenWidthModifier()),
|
setSize(std::max(mTextCache->metrics.size.x() + (12.0f * Renderer::getScreenWidthModifier()),
|
||||||
minWidth), mTextCache->metrics.size.y());
|
minWidth),
|
||||||
|
mTextCache->metrics.size.y());
|
||||||
|
|
||||||
updateHelpPrompts();
|
updateHelpPrompts();
|
||||||
}
|
}
|
||||||
|
@ -103,14 +101,14 @@ void ButtonComponent::render(const Transform4x4f& parentTrans)
|
||||||
|
|
||||||
if (mTextCache) {
|
if (mTextCache) {
|
||||||
Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2.0f,
|
Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2.0f,
|
||||||
(mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0);
|
(mSize.y() - mTextCache->metrics.size.y()) / 2.0f, 0);
|
||||||
trans = trans.translate(centerOffset);
|
trans = trans.translate(centerOffset);
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("DebugText")) {
|
if (Settings::getInstance()->getBool("DebugText")) {
|
||||||
Renderer::drawRect(centerOffset.x(), 0.0f, mTextCache->metrics.size.x(),
|
Renderer::drawRect(centerOffset.x(), 0.0f, mTextCache->metrics.size.x(), mSize.y(),
|
||||||
mSize.y(), 0x00000033, 0x00000033);
|
0x00000033, 0x00000033);
|
||||||
Renderer::drawRect(mBox.getPosition().x(), 0.0f, mBox.getSize().x(),
|
Renderer::drawRect(mBox.getPosition().x(), 0.0f, mBox.getSize().x(), mSize.y(),
|
||||||
mSize.y(), 0x0000FF33, 0x0000FF33);
|
0x0000FF33, 0x0000FF33);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
|
@ -9,18 +9,20 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
#define ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
||||||
|
|
||||||
#include "components/NinePatchComponent.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "components/NinePatchComponent.h"
|
||||||
|
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
|
||||||
class ButtonComponent : public GuiComponent
|
class ButtonComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ButtonComponent(Window* window, const std::string& text = "",
|
ButtonComponent(Window* window,
|
||||||
const std::string& helpText = "", const std::function<void()>& func = nullptr);
|
const std::string& text = "",
|
||||||
|
const std::string& helpText = "",
|
||||||
|
const std::function<void()>& func = nullptr);
|
||||||
|
|
||||||
void setPressedFunc(std::function<void()> f);
|
void setPressedFunc(std::function<void()> f) { mPressedFunc = f; }
|
||||||
void setEnabled(bool state) override;
|
void setEnabled(bool state) override;
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
|
@ -28,8 +30,8 @@ public:
|
||||||
|
|
||||||
void setText(const std::string& text, const std::string& helpText);
|
void setText(const std::string& text, const std::string& helpText);
|
||||||
|
|
||||||
inline const std::string& getText() const { return mText; };
|
const std::string& getText() const { return mText; }
|
||||||
inline const std::function<void()>& getPressedFunc() const { return mPressedFunc; };
|
const std::function<void()>& getPressedFunc() const { return mPressedFunc; }
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void onFocusGained() override;
|
void onFocusGained() override;
|
||||||
|
|
|
@ -12,12 +12,10 @@
|
||||||
|
|
||||||
using namespace GridFlags;
|
using namespace GridFlags;
|
||||||
|
|
||||||
ComponentGrid::ComponentGrid(
|
ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions)
|
||||||
Window* window,
|
: GuiComponent(window)
|
||||||
const Vector2i& gridDimensions)
|
, mGridSize(gridDimensions)
|
||||||
: GuiComponent(window),
|
, mCursor(0, 0)
|
||||||
mGridSize(gridDimensions),
|
|
||||||
mCursor(0, 0)
|
|
||||||
{
|
{
|
||||||
assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
|
assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
|
||||||
|
|
||||||
|
@ -25,6 +23,7 @@ ComponentGrid::ComponentGrid(
|
||||||
|
|
||||||
mColWidths = new float[gridDimensions.x()];
|
mColWidths = new float[gridDimensions.x()];
|
||||||
mRowHeights = new float[gridDimensions.y()];
|
mRowHeights = new float[gridDimensions.y()];
|
||||||
|
|
||||||
for (int x = 0; x < gridDimensions.x(); x++)
|
for (int x = 0; x < gridDimensions.x(); x++)
|
||||||
mColWidths[x] = 0;
|
mColWidths[x] = 0;
|
||||||
for (int y = 0; y < gridDimensions.y(); y++)
|
for (int y = 0; y < gridDimensions.y(); y++)
|
||||||
|
@ -90,14 +89,13 @@ void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComponentGrid::setEntry(
|
void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp,
|
||||||
const std::shared_ptr<GuiComponent>& comp,
|
const Vector2i& pos,
|
||||||
const Vector2i& pos,
|
bool canFocus,
|
||||||
bool canFocus,
|
bool resize,
|
||||||
bool resize,
|
const Vector2i& size,
|
||||||
const Vector2i& size,
|
unsigned int border,
|
||||||
unsigned int border,
|
GridFlags::UpdateType updateType)
|
||||||
GridFlags::UpdateType updateType)
|
|
||||||
{
|
{
|
||||||
assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
|
assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
|
||||||
assert(comp != nullptr);
|
assert(comp != nullptr);
|
||||||
|
@ -294,14 +292,14 @@ bool ComponentGrid::moveCursor(Vector2i dir)
|
||||||
Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
|
Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
|
||||||
|
|
||||||
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
|
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
|
||||||
mCursor.y() < mGridSize.y()) {
|
mCursor.y() < mGridSize.y()) {
|
||||||
mCursor = mCursor + dir;
|
mCursor = mCursor + dir;
|
||||||
Vector2i curDirPos = mCursor;
|
Vector2i curDirPos = mCursor;
|
||||||
const GridEntry* cursorEntry;
|
const GridEntry* cursorEntry;
|
||||||
|
|
||||||
// Spread out on search axis+
|
// Spread out on search axis+
|
||||||
while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()
|
while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() && mCursor.x() >= 0 &&
|
||||||
&& mCursor.x() >= 0 && mCursor.y() >= 0) {
|
mCursor.y() >= 0) {
|
||||||
cursorEntry = getCellAt(mCursor);
|
cursorEntry = getCellAt(mCursor);
|
||||||
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
|
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
|
||||||
onCursorMoved(origCursor, mCursor);
|
onCursorMoved(origCursor, mCursor);
|
||||||
|
@ -312,8 +310,8 @@ bool ComponentGrid::moveCursor(Vector2i dir)
|
||||||
|
|
||||||
// Now again on search axis-
|
// Now again on search axis-
|
||||||
mCursor = curDirPos;
|
mCursor = curDirPos;
|
||||||
while (mCursor.x() >= 0 && mCursor.y() >= 0
|
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
|
||||||
&& mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) {
|
mCursor.y() < mGridSize.y()) {
|
||||||
cursorEntry = getCellAt(mCursor);
|
cursorEntry = getCellAt(mCursor);
|
||||||
|
|
||||||
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
|
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
|
||||||
|
@ -356,8 +354,9 @@ void ComponentGrid::update(int deltaTime)
|
||||||
const GridEntry* cursorEntry = getCellAt(mCursor);
|
const GridEntry* cursorEntry = getCellAt(mCursor);
|
||||||
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
|
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
|
||||||
if (it->updateType == UPDATE_ALWAYS ||
|
if (it->updateType == UPDATE_ALWAYS ||
|
||||||
(it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
|
(it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it))) {
|
||||||
it->component->update(deltaTime);
|
it->component->update(deltaTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +370,7 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
|
||||||
for (size_t i = 0; i < mSeparators.size(); i++) {
|
for (size_t i = 0; i < mSeparators.size(); i++) {
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
Renderer::drawRect(mSeparators[i][0], mSeparators[i][1], mSeparators[i][2],
|
Renderer::drawRect(mSeparators[i][0], mSeparators[i][1], mSeparators[i][2],
|
||||||
mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF);
|
mSeparators[i][3], 0xC6C7C6FF, 0xC6C7C6FF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,14 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_COMPONENT_GRID_H
|
#ifndef ES_CORE_COMPONENTS_COMPONENT_GRID_H
|
||||||
#define ES_CORE_COMPONENTS_COMPONENT_GRID_H
|
#define ES_CORE_COMPONENTS_COMPONENT_GRID_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "math/Vector2i.h"
|
#include "math/Vector2i.h"
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
namespace GridFlags
|
namespace GridFlags
|
||||||
{
|
{
|
||||||
enum UpdateType {
|
enum UpdateType {
|
||||||
UPDATE_ALWAYS,
|
UPDATE_ALWAYS, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
UPDATE_WHEN_SELECTED,
|
UPDATE_WHEN_SELECTED,
|
||||||
UPDATE_NEVER
|
UPDATE_NEVER
|
||||||
};
|
};
|
||||||
|
@ -28,7 +28,7 @@ namespace GridFlags
|
||||||
BORDER_LEFT = 4,
|
BORDER_LEFT = 4,
|
||||||
BORDER_RIGHT = 8
|
BORDER_RIGHT = 8
|
||||||
};
|
};
|
||||||
};
|
}; // namespace GridFlags
|
||||||
|
|
||||||
// Provides basic layout of components in an X*Y grid.
|
// Provides basic layout of components in an X*Y grid.
|
||||||
class ComponentGrid : public GuiComponent
|
class ComponentGrid : public GuiComponent
|
||||||
|
@ -39,14 +39,13 @@ public:
|
||||||
|
|
||||||
bool removeEntry(const std::shared_ptr<GuiComponent>& comp);
|
bool removeEntry(const std::shared_ptr<GuiComponent>& comp);
|
||||||
|
|
||||||
void setEntry(
|
void setEntry(const std::shared_ptr<GuiComponent>& comp,
|
||||||
const std::shared_ptr<GuiComponent>& comp,
|
const Vector2i& pos,
|
||||||
const Vector2i& pos,
|
bool canFocus,
|
||||||
bool canFocus,
|
bool resize = true,
|
||||||
bool resize = true,
|
const Vector2i& size = Vector2i(1, 1),
|
||||||
const Vector2i& size = Vector2i(1, 1),
|
unsigned int border = GridFlags::BORDER_NONE,
|
||||||
unsigned int border = GridFlags::BORDER_NONE,
|
GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS);
|
||||||
GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS);
|
|
||||||
|
|
||||||
void textInput(const std::string& text) override;
|
void textInput(const std::string& text) override;
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
|
@ -69,7 +68,7 @@ public:
|
||||||
bool moveCursor(Vector2i dir);
|
bool moveCursor(Vector2i dir);
|
||||||
void setCursorTo(const std::shared_ptr<GuiComponent>& comp);
|
void setCursorTo(const std::shared_ptr<GuiComponent>& comp);
|
||||||
|
|
||||||
inline std::shared_ptr<GuiComponent> getSelectedComponent()
|
std::shared_ptr<GuiComponent> getSelectedComponent()
|
||||||
{
|
{
|
||||||
const GridEntry* e = getCellAt(mCursor);
|
const GridEntry* e = getCellAt(mCursor);
|
||||||
if (e)
|
if (e)
|
||||||
|
@ -95,28 +94,24 @@ private:
|
||||||
GridFlags::UpdateType updateType;
|
GridFlags::UpdateType updateType;
|
||||||
unsigned int border;
|
unsigned int border;
|
||||||
|
|
||||||
GridEntry(
|
GridEntry(const Vector2i& p = Vector2i::Zero(),
|
||||||
const Vector2i& p = Vector2i::Zero(),
|
const Vector2i& d = Vector2i::Zero(),
|
||||||
const Vector2i& d = Vector2i::Zero(),
|
const std::shared_ptr<GuiComponent>& cmp = nullptr,
|
||||||
const std::shared_ptr<GuiComponent>& cmp = nullptr,
|
bool f = false,
|
||||||
bool f = false,
|
bool r = true,
|
||||||
bool r = true,
|
GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS,
|
||||||
GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS,
|
unsigned int b = GridFlags::BORDER_NONE)
|
||||||
unsigned int b =
|
: pos(p)
|
||||||
GridFlags::BORDER_NONE)
|
, dim(d)
|
||||||
: pos(p),
|
, component(cmp)
|
||||||
dim(d),
|
, canFocus(f)
|
||||||
component(cmp),
|
, resize(r)
|
||||||
canFocus(f),
|
, updateType(u)
|
||||||
resize(r),
|
, border(b)
|
||||||
updateType(u),
|
|
||||||
border(b)
|
|
||||||
{};
|
|
||||||
|
|
||||||
operator bool() const
|
|
||||||
{
|
{
|
||||||
return component != nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
operator bool() const { return component != nullptr; }
|
||||||
};
|
};
|
||||||
|
|
||||||
// Update position and size.
|
// Update position and size.
|
||||||
|
@ -125,9 +120,7 @@ private:
|
||||||
|
|
||||||
void onCursorMoved(Vector2i from, Vector2i to);
|
void onCursorMoved(Vector2i from, Vector2i to);
|
||||||
const GridEntry* getCellAt(int x, int y) const;
|
const GridEntry* getCellAt(int x, int y) const;
|
||||||
|
const GridEntry* getCellAt(const Vector2i& pos) const { return getCellAt(pos.x(), pos.y()); }
|
||||||
inline const GridEntry* getCellAt(const Vector2i& pos) const
|
|
||||||
{ return getCellAt(pos.x(), pos.y()); }
|
|
||||||
|
|
||||||
std::vector<std::vector<float>> mSeparators;
|
std::vector<std::vector<float>> mSeparators;
|
||||||
Vector2i mGridSize;
|
Vector2i mGridSize;
|
||||||
|
|
|
@ -10,14 +10,14 @@
|
||||||
|
|
||||||
#define TOTAL_HORIZONTAL_PADDING_PX 20.0f
|
#define TOTAL_HORIZONTAL_PADDING_PX 20.0f
|
||||||
|
|
||||||
ComponentList::ComponentList(Window* window) : IList<ComponentListRow,
|
ComponentList::ComponentList(Window* window)
|
||||||
void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
|
: IList<ComponentListRow, void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
|
||||||
{
|
{
|
||||||
// Adjust the padding relative to the aspect ratio and screen resolution to make it look
|
// Adjust the padding relative to the aspect ratio and screen resolution to make it look
|
||||||
// coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
// coherent regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
||||||
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
||||||
mHorizontalPadding = TOTAL_HORIZONTAL_PADDING_PX * aspectValue *
|
mHorizontalPadding =
|
||||||
Renderer::getScreenWidthModifier();
|
TOTAL_HORIZONTAL_PADDING_PX * aspectValue * Renderer::getScreenWidthModifier();
|
||||||
|
|
||||||
mSelectorBarOffset = 0.0f;
|
mSelectorBarOffset = 0.0f;
|
||||||
mCameraOffset = 0.0f;
|
mCameraOffset = 0.0f;
|
||||||
|
@ -34,7 +34,7 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
|
||||||
this->add(e);
|
this->add(e);
|
||||||
|
|
||||||
for (auto it = mEntries.back().data.elements.cbegin();
|
for (auto it = mEntries.back().data.elements.cbegin();
|
||||||
it != mEntries.back().data.elements.cend(); it++)
|
it != mEntries.back().data.elements.cend(); it++)
|
||||||
addChild(it->component.get());
|
addChild(it->component.get());
|
||||||
|
|
||||||
updateElementSize(mEntries.back().data);
|
updateElementSize(mEntries.back().data);
|
||||||
|
@ -56,16 +56,6 @@ void ComponentList::onSizeChanged()
|
||||||
updateCameraOffset();
|
updateCameraOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComponentList::onFocusLost()
|
|
||||||
{
|
|
||||||
mFocused = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ComponentList::onFocusGained()
|
|
||||||
{
|
|
||||||
mFocused = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ComponentList::input(InputConfig* config, Input input)
|
bool ComponentList::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if (size() == 0)
|
if (size() == 0)
|
||||||
|
@ -122,7 +112,7 @@ void ComponentList::update(int deltaTime)
|
||||||
if (size()) {
|
if (size()) {
|
||||||
// Update our currently selected row.
|
// Update our currently selected row.
|
||||||
for (auto it = mEntries.at(mCursor).data.elements.cbegin();
|
for (auto it = mEntries.at(mCursor).data.elements.cbegin();
|
||||||
it != mEntries.at(mCursor).data.elements.cend(); it++)
|
it != mEntries.at(mCursor).data.elements.cend(); it++)
|
||||||
it->component->update(deltaTime);
|
it->component->update(deltaTime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,8 +146,8 @@ void ComponentList::updateCameraOffset()
|
||||||
// Move the camera to scroll.
|
// Move the camera to scroll.
|
||||||
const float totalHeight = getTotalRowHeight();
|
const float totalHeight = getTotalRowHeight();
|
||||||
if (totalHeight > mSize.y()) {
|
if (totalHeight > mSize.y()) {
|
||||||
float target = mSelectorBarOffset +
|
float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data) / 2.0f -
|
||||||
getRowHeight(mEntries.at(mCursor).data) / 2.0f - (mSize.y() / 2.0f);
|
(mSize.y() / 2.0f);
|
||||||
|
|
||||||
// Clamp the camera to prevent a fraction of a row from being displayed.
|
// Clamp the camera to prevent a fraction of a row from being displayed.
|
||||||
mCameraOffset = 0.0f;
|
mCameraOffset = 0.0f;
|
||||||
|
@ -187,9 +177,10 @@ void ComponentList::render(const Transform4x4f& parentTrans)
|
||||||
// Clip everything to be inside our bounds.
|
// Clip everything to be inside our bounds.
|
||||||
Vector3f dim(mSize.x(), mSize.y(), 0.0f);
|
Vector3f dim(mSize.x(), mSize.y(), 0.0f);
|
||||||
dim = trans * dim - trans.translation();
|
dim = trans * dim - trans.translation();
|
||||||
Renderer::pushClipRect(Vector2i(static_cast<int>(std::round(trans.translation().x())),
|
Renderer::pushClipRect(
|
||||||
static_cast<int>(std::round(trans.translation().y()))), Vector2i(static_cast<int>(
|
Vector2i(static_cast<int>(std::round(trans.translation().x())),
|
||||||
std::round(dim.x())), static_cast<int>(std::round(dim.y()))));
|
static_cast<int>(std::round(trans.translation().y()))),
|
||||||
|
Vector2i(static_cast<int>(std::round(dim.x())), static_cast<int>(std::round(dim.y()))));
|
||||||
|
|
||||||
// Scroll the camera.
|
// Scroll the camera.
|
||||||
trans.translate(Vector3f(0.0f, -std::round(mCameraOffset), 0.0f));
|
trans.translate(Vector3f(0.0f, -std::round(mCameraOffset), 0.0f));
|
||||||
|
@ -205,7 +196,7 @@ void ComponentList::render(const Transform4x4f& parentTrans)
|
||||||
// For the row where the cursor is at, we want to remove any hue from the
|
// For the row where the cursor is at, we want to remove any hue from the
|
||||||
// font or image before inverting, as it would otherwise lead to an ugly
|
// font or image before inverting, as it would otherwise lead to an ugly
|
||||||
// inverted color (e.g. red inverting to a green hue).
|
// inverted color (e.g. red inverting to a green hue).
|
||||||
if (i == mCursor && it->component->getValue() != "" ) {
|
if (i == mCursor && it->component->getValue() != "") {
|
||||||
// Check if we're dealing with text or an image component.
|
// Check if we're dealing with text or an image component.
|
||||||
bool isTextComponent = true;
|
bool isTextComponent = true;
|
||||||
unsigned int origColor = it->component->getColor();
|
unsigned int origColor = it->component->getColor();
|
||||||
|
@ -260,13 +251,13 @@ void ComponentList::render(const Transform4x4f& parentTrans)
|
||||||
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data);
|
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data);
|
||||||
|
|
||||||
if (opacity == 1) {
|
if (opacity == 1) {
|
||||||
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(),
|
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0xFFFFFFFF,
|
||||||
selectedRowHeight, 0xFFFFFFFF, 0xFFFFFFFF, false, opacity, trans,
|
0xFFFFFFFF, false, opacity, trans,
|
||||||
Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
|
Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
|
||||||
|
|
||||||
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(),
|
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0x777777FF,
|
||||||
selectedRowHeight, 0x777777FF, 0x777777FF, false, opacity, trans,
|
0x777777FF, false, opacity, trans, Renderer::Blend::ONE,
|
||||||
Renderer::Blend::ONE, Renderer::Blend::ONE);
|
Renderer::Blend::ONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++)
|
for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++)
|
||||||
|
@ -281,12 +272,12 @@ void ComponentList::render(const Transform4x4f& parentTrans)
|
||||||
float y = 0;
|
float y = 0;
|
||||||
for (unsigned int i = 0; i < mEntries.size(); i++) {
|
for (unsigned int i = 0; i < mEntries.size(); i++) {
|
||||||
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(),
|
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(),
|
||||||
0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
|
0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
|
||||||
y += getRowHeight(mEntries.at(i).data);
|
y += getRowHeight(mEntries.at(i).data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(),
|
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f * Renderer::getScreenHeightModifier(), 0xC6C7C6FF,
|
||||||
0xC6C7C6FF, 0xC6C7C6FF, false, opacity, trans);
|
0xC6C7C6FF, false, opacity, trans);
|
||||||
Renderer::popClipRect();
|
Renderer::popClipRect();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,7 +353,7 @@ std::vector<HelpPrompt> ComponentList::getHelpPrompts()
|
||||||
return std::vector<HelpPrompt>();
|
return std::vector<HelpPrompt>();
|
||||||
|
|
||||||
std::vector<HelpPrompt> prompts =
|
std::vector<HelpPrompt> prompts =
|
||||||
mEntries.at(mCursor).data.elements.back().component->getHelpPrompts();
|
mEntries.at(mCursor).data.elements.back().component->getHelpPrompts();
|
||||||
|
|
||||||
if (size() > 1) {
|
if (size() > 1) {
|
||||||
bool addMovePrompt = true;
|
bool addMovePrompt = true;
|
||||||
|
|
|
@ -12,21 +12,21 @@
|
||||||
#include "IList.h"
|
#include "IList.h"
|
||||||
|
|
||||||
struct ComponentListElement {
|
struct ComponentListElement {
|
||||||
ComponentListElement(
|
ComponentListElement(const std::shared_ptr<GuiComponent>& cmp = nullptr,
|
||||||
const std::shared_ptr<GuiComponent>& cmp = nullptr,
|
bool resize_w = true,
|
||||||
bool resize_w = true,
|
bool inv = true)
|
||||||
bool inv = true)
|
: component(cmp)
|
||||||
: component(cmp),
|
, resize_width(resize_w)
|
||||||
resize_width(resize_w),
|
, invert_when_selected(inv)
|
||||||
invert_when_selected(inv) {};
|
{
|
||||||
|
}
|
||||||
|
|
||||||
std::shared_ptr<GuiComponent> component;
|
std::shared_ptr<GuiComponent> component;
|
||||||
bool resize_width;
|
bool resize_width;
|
||||||
bool invert_when_selected;
|
bool invert_when_selected;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ComponentListRow
|
struct ComponentListRow {
|
||||||
{
|
|
||||||
std::vector<ComponentListElement> elements;
|
std::vector<ComponentListElement> elements;
|
||||||
|
|
||||||
// The input handler is called when the user enters any input while this row is
|
// The input handler is called when the user enters any input while this row is
|
||||||
|
@ -36,14 +36,15 @@ struct ComponentListRow
|
||||||
// to forward the input to the rightmost element in the currently selected row.
|
// to forward the input to the rightmost element in the currently selected row.
|
||||||
std::function<bool(InputConfig*, Input)> input_handler;
|
std::function<bool(InputConfig*, Input)> input_handler;
|
||||||
|
|
||||||
inline void addElement(const std::shared_ptr<GuiComponent>& component,
|
void addElement(const std::shared_ptr<GuiComponent>& component,
|
||||||
bool resize_width, bool invert_when_selected = true)
|
bool resize_width,
|
||||||
|
bool invert_when_selected = true)
|
||||||
{
|
{
|
||||||
elements.push_back(ComponentListElement(component, resize_width, invert_when_selected));
|
elements.push_back(ComponentListElement(component, resize_width, invert_when_selected));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility function for making an input handler for "when the users presses A on this, do func".
|
// Utility function for making an input handler for "when the users presses A on this, do func".
|
||||||
inline void makeAcceptInputHandler(const std::function<void()>& func)
|
void makeAcceptInputHandler(const std::function<void()>& func)
|
||||||
{
|
{
|
||||||
input_handler = [func](InputConfig* config, Input input) -> bool {
|
input_handler = [func](InputConfig* config, Input input) -> bool {
|
||||||
if (config->isMappedTo("a", input) && input.value != 0) {
|
if (config->isMappedTo("a", input) && input.value != 0) {
|
||||||
|
@ -69,19 +70,23 @@ public:
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void onFocusGained() override;
|
void onFocusGained() override { mFocused = true; }
|
||||||
void onFocusLost() override;
|
void onFocusLost() override { mFocused = false; }
|
||||||
|
|
||||||
bool moveCursor(int amt);
|
bool moveCursor(int amt);
|
||||||
inline int getCursorId() const { return mCursor; }
|
int getCursorId() const { return mCursor; }
|
||||||
|
|
||||||
float getTotalRowHeight() const;
|
float getTotalRowHeight() const;
|
||||||
inline float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); }
|
float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); }
|
||||||
|
|
||||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& callback)
|
void setCursorChangedCallback(const std::function<void(CursorState state)>& callback)
|
||||||
{ mCursorChangedCallback = callback; };
|
{
|
||||||
inline const std::function<void(CursorState state)>& getCursorChangedCallback() const
|
mCursorChangedCallback = callback;
|
||||||
{ return mCursorChangedCallback; };
|
}
|
||||||
|
const std::function<void(CursorState state)>& getCursorChangedCallback() const
|
||||||
|
{
|
||||||
|
return mCursorChangedCallback;
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onCursorChanged(const CursorState& state) override;
|
void onCursorChanged(const CursorState& state) override;
|
||||||
|
|
|
@ -10,28 +10,28 @@
|
||||||
|
|
||||||
#include "components/DateTimeComponent.h"
|
#include "components/DateTimeComponent.h"
|
||||||
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
DateTimeComponent::DateTimeComponent(Window* window)
|
DateTimeComponent::DateTimeComponent(Window* window)
|
||||||
: TextComponent(window), mDisplayRelative(false)
|
: TextComponent(window)
|
||||||
|
, mDisplayRelative(false)
|
||||||
{
|
{
|
||||||
// ISO 8601 date format.
|
// ISO 8601 date format.
|
||||||
setFormat("%Y-%m-%d");
|
setFormat("%Y-%m-%d");
|
||||||
}
|
}
|
||||||
|
|
||||||
DateTimeComponent::DateTimeComponent(
|
DateTimeComponent::DateTimeComponent(Window* window,
|
||||||
Window* window,
|
const std::string& text,
|
||||||
const std::string& text,
|
const std::shared_ptr<Font>& font,
|
||||||
const std::shared_ptr<Font>& font,
|
unsigned int color,
|
||||||
unsigned int color,
|
Alignment align,
|
||||||
Alignment align,
|
Vector3f pos,
|
||||||
Vector3f pos,
|
Vector2f size,
|
||||||
Vector2f size,
|
unsigned int bgcolor)
|
||||||
unsigned int bgcolor)
|
: TextComponent(window, text, font, color, align, pos, size, bgcolor)
|
||||||
: TextComponent(window, text, font, color, align, pos, size, bgcolor),
|
, mDisplayRelative(false)
|
||||||
mDisplayRelative(false)
|
|
||||||
{
|
{
|
||||||
// ISO 8601 date format.
|
// ISO 8601 date format.
|
||||||
setFormat("%Y-%m-%d");
|
setFormat("%Y-%m-%d");
|
||||||
|
@ -45,6 +45,7 @@ void DateTimeComponent::setValue(const std::string& val)
|
||||||
|
|
||||||
std::string DateTimeComponent::getValue() const
|
std::string DateTimeComponent::getValue() const
|
||||||
{
|
{
|
||||||
|
// Return time value as a string.
|
||||||
return mTime;
|
return mTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,17 +80,17 @@ std::string DateTimeComponent::getDisplayString() const
|
||||||
std::string buf;
|
std::string buf;
|
||||||
|
|
||||||
if (dur.getDays() > 0)
|
if (dur.getDays() > 0)
|
||||||
buf = std::to_string(dur.getDays()) + " day" +
|
buf = std::to_string(dur.getDays()) + " day" + // Line break.
|
||||||
(dur.getDays() > 1 ? "s" : "") + " ago";
|
(dur.getDays() > 1 ? "s" : "") + " ago";
|
||||||
else if (dur.getHours() > 0)
|
else if (dur.getHours() > 0)
|
||||||
buf = std::to_string(dur.getHours()) + " hour" +
|
buf = std::to_string(dur.getHours()) + " hour" + // Line break.
|
||||||
(dur.getHours() > 1 ? "s" : "") + " ago";
|
(dur.getHours() > 1 ? "s" : "") + " ago";
|
||||||
else if (dur.getMinutes() > 0)
|
else if (dur.getMinutes() > 0)
|
||||||
buf = std::to_string(dur.getMinutes()) + " minute" +
|
buf = std::to_string(dur.getMinutes()) + " minute" + // Line break.
|
||||||
(dur.getMinutes() > 1 ? "s" : "") + " ago";
|
(dur.getMinutes() > 1 ? "s" : "") + " ago";
|
||||||
else
|
else
|
||||||
buf = std::to_string(dur.getSeconds()) + " second" +
|
buf = std::to_string(dur.getSeconds()) + " second" + // Line break.
|
||||||
(dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago";
|
(dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago";
|
||||||
|
|
||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
}
|
}
|
||||||
|
@ -106,7 +107,9 @@ void DateTimeComponent::render(const Transform4x4f& parentTrans)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
|
@ -140,7 +143,7 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
else if (str == "right")
|
else if (str == "right")
|
||||||
setHorizontalAlignment(ALIGN_RIGHT);
|
setHorizontalAlignment(ALIGN_RIGHT);
|
||||||
else
|
else
|
||||||
LOG(LogError) << "Unknown text alignment string: " << str;
|
LOG(LogError) << "Unknown text alignment string: " << str;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
||||||
|
|
|
@ -11,8 +11,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
#define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
|
||||||
|
|
||||||
#include "utils/TimeUtil.h"
|
|
||||||
#include "TextComponent.h"
|
#include "TextComponent.h"
|
||||||
|
#include "utils/TimeUtil.h"
|
||||||
|
|
||||||
class ThemeData;
|
class ThemeData;
|
||||||
|
|
||||||
|
@ -21,15 +21,14 @@ class DateTimeComponent : public TextComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
DateTimeComponent(Window* window);
|
DateTimeComponent(Window* window);
|
||||||
DateTimeComponent(
|
DateTimeComponent(Window* window,
|
||||||
Window* window,
|
const std::string& text,
|
||||||
const std::string& text,
|
const std::shared_ptr<Font>& font,
|
||||||
const std::shared_ptr<Font>& font,
|
unsigned int color = 0x000000FF,
|
||||||
unsigned int color = 0x000000FF,
|
Alignment align = ALIGN_LEFT,
|
||||||
Alignment align = ALIGN_LEFT,
|
Vector3f pos = Vector3f::Zero(),
|
||||||
Vector3f pos = Vector3f::Zero(),
|
Vector2f size = Vector2f::Zero(),
|
||||||
Vector2f size = Vector2f::Zero(),
|
unsigned int bgcolor = 0x00000000);
|
||||||
unsigned int bgcolor = 0x00000000);
|
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
|
@ -39,8 +38,10 @@ public:
|
||||||
void setFormat(const std::string& format);
|
void setFormat(const std::string& format);
|
||||||
void setDisplayRelative(bool displayRelative);
|
void setDisplayRelative(bool displayRelative);
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void onTextChanged() override;
|
void onTextChanged() override;
|
||||||
|
|
|
@ -8,24 +8,21 @@
|
||||||
|
|
||||||
#include "components/DateTimeEditComponent.h"
|
#include "components/DateTimeEditComponent.h"
|
||||||
|
|
||||||
|
#include "Settings.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Settings.h"
|
|
||||||
|
|
||||||
DateTimeEditComponent::DateTimeEditComponent(
|
DateTimeEditComponent::DateTimeEditComponent(Window* window, bool alignRight, DisplayMode dispMode)
|
||||||
Window* window,
|
: GuiComponent(window)
|
||||||
bool alignRight,
|
, mEditing(false)
|
||||||
DisplayMode dispMode)
|
, mEditIndex(0)
|
||||||
: GuiComponent(window),
|
, mDisplayMode(dispMode)
|
||||||
mEditing(false),
|
, mRelativeUpdateAccumulator(0)
|
||||||
mEditIndex(0),
|
, mColor(0x777777FF)
|
||||||
mDisplayMode(dispMode),
|
, mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT))
|
||||||
mRelativeUpdateAccumulator(0),
|
, mUppercase(false)
|
||||||
mColor(0x777777FF),
|
, mAutoSize(true)
|
||||||
mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)),
|
, mAlignRight(alignRight)
|
||||||
mUppercase(false),
|
|
||||||
mAutoSize(true),
|
|
||||||
mAlignRight(alignRight)
|
|
||||||
{
|
{
|
||||||
updateTextCache();
|
updateTextCache();
|
||||||
}
|
}
|
||||||
|
@ -68,8 +65,9 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
|
||||||
if (mEditing) {
|
if (mEditing) {
|
||||||
|
|
||||||
if (config->isMappedLike("lefttrigger", input) ||
|
if (config->isMappedLike("lefttrigger", input) ||
|
||||||
config->isMappedLike("righttrigger", input))
|
config->isMappedLike("righttrigger", input)) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (config->isMappedTo("b", input)) {
|
if (config->isMappedTo("b", input)) {
|
||||||
mEditing = false;
|
mEditing = false;
|
||||||
|
@ -101,23 +99,21 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
|
||||||
new_tm.tm_mon = 0;
|
new_tm.tm_mon = 0;
|
||||||
else if (new_tm.tm_mon < 0)
|
else if (new_tm.tm_mon < 0)
|
||||||
new_tm.tm_mon = 11;
|
new_tm.tm_mon = 11;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (mEditIndex == 2) {
|
else if (mEditIndex == 2) {
|
||||||
const int days_in_month =
|
const int days_in_month =
|
||||||
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||||
new_tm.tm_mday += incDir;
|
new_tm.tm_mday += incDir;
|
||||||
|
|
||||||
if (new_tm.tm_mday > days_in_month)
|
if (new_tm.tm_mday > days_in_month)
|
||||||
new_tm.tm_mday = 1;
|
new_tm.tm_mday = 1;
|
||||||
else if (new_tm.tm_mday < 1)
|
else if (new_tm.tm_mday < 1)
|
||||||
new_tm.tm_mday = days_in_month;
|
new_tm.tm_mday = days_in_month;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate day.
|
// Validate day.
|
||||||
const int days_in_month =
|
const int days_in_month =
|
||||||
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
|
||||||
if (new_tm.tm_mday > days_in_month)
|
if (new_tm.tm_mday > days_in_month)
|
||||||
new_tm.tm_mday = days_in_month;
|
new_tm.tm_mday = days_in_month;
|
||||||
|
|
||||||
|
@ -185,11 +181,11 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
|
||||||
if (Settings::getInstance()->getBool("DebugText")) {
|
if (Settings::getInstance()->getBool("DebugText")) {
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
if (mTextCache->metrics.size.x() > 0) {
|
if (mTextCache->metrics.size.x() > 0) {
|
||||||
Renderer::drawRect(0.0f, 0.0f - off.y(),
|
Renderer::drawRect(0.0f, 0.0f - off.y(), mSize.x() - off.x(), mSize.y(), 0x0000FF33,
|
||||||
mSize.x() - off.x(), mSize.y(), 0x0000FF33, 0x0000FF33);
|
0x0000FF33);
|
||||||
}
|
}
|
||||||
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
||||||
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
||||||
}
|
}
|
||||||
|
|
||||||
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
|
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
|
||||||
|
@ -198,8 +194,8 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
|
||||||
if (mEditing) {
|
if (mEditing) {
|
||||||
if (mEditIndex >= 0 && static_cast<unsigned int>(mEditIndex) < mCursorBoxes.size())
|
if (mEditIndex >= 0 && static_cast<unsigned int>(mEditIndex) < mCursorBoxes.size())
|
||||||
Renderer::drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1],
|
Renderer::drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1],
|
||||||
mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3],
|
mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3],
|
||||||
0x00000022, 0x00000022);
|
0x00000022, 0x00000022);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,41 +207,24 @@ void DateTimeEditComponent::setValue(const std::string& val)
|
||||||
updateTextCache();
|
updateTextCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string DateTimeEditComponent::getValue() const
|
|
||||||
{
|
|
||||||
return mTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode() const
|
|
||||||
{
|
|
||||||
// if (mEditing) {
|
|
||||||
// if (mDisplayMode == DISP_RELATIVE_TO_NOW) {
|
|
||||||
// // TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME.
|
|
||||||
// return DISP_DATE;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return mDisplayMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
|
std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
|
||||||
{
|
{
|
||||||
// ISO 8601 date format.
|
// ISO 8601 date format.
|
||||||
std::string fmt;
|
std::string fmt;
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case DISP_DATE: {
|
case DISP_DATE: {
|
||||||
if (mTime.getTime() == 0)
|
if (mTime.getTime() == 0)
|
||||||
return "unknown";
|
return "unknown";
|
||||||
fmt = "%Y-%m-%d";
|
fmt = "%Y-%m-%d";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DISP_DATE_TIME: {
|
case DISP_DATE_TIME: {
|
||||||
if (mTime.getTime() == 0)
|
if (mTime.getTime() == 0)
|
||||||
return "unknown";
|
return "unknown";
|
||||||
fmt = "%Y-%m-%d %H:%M:%S";
|
fmt = "%Y-%m-%d %H:%M:%S";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case DISP_RELATIVE_TO_NOW: {
|
case DISP_RELATIVE_TO_NOW: {
|
||||||
// Relative time.
|
// Relative time.
|
||||||
if (mTime.getTime() == 0)
|
if (mTime.getTime() == 0)
|
||||||
return "never";
|
return "never";
|
||||||
|
@ -256,21 +235,22 @@ std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
|
||||||
std::string buf;
|
std::string buf;
|
||||||
|
|
||||||
if (dur.getDays() > 0)
|
if (dur.getDays() > 0)
|
||||||
buf = std::to_string(dur.getDays()) + " day" +
|
buf = std::to_string(dur.getDays()) + // Line break.
|
||||||
(dur.getDays() > 1 ? "s" : "") + " ago";
|
" day" + (dur.getDays() > 1 ? "s" : "") + " ago";
|
||||||
else if (dur.getHours() > 0)
|
else if (dur.getHours() > 0)
|
||||||
buf = std::to_string(dur.getHours()) + " hour" +
|
buf = std::to_string(dur.getHours()) + // Line break.
|
||||||
(dur.getHours() > 1 ? "s" : "") + " ago";
|
" hour" + (dur.getHours() > 1 ? "s" : "") + " ago";
|
||||||
else if (dur.getMinutes() > 0)
|
else if (dur.getMinutes() > 0)
|
||||||
buf = std::to_string(dur.getMinutes()) + " minute" +
|
buf = std::to_string(dur.getMinutes()) + // Line break.
|
||||||
(dur.getMinutes() > 1 ? "s" : "") + " ago";
|
" minute" + (dur.getMinutes() > 1 ? "s" : "") + " ago";
|
||||||
else
|
else
|
||||||
buf = std::to_string(dur.getSeconds()) + " second" +
|
buf = std::to_string(dur.getSeconds()) + // Line break.
|
||||||
(dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") + " ago";
|
" second" + (dur.getSeconds() > 1 || dur.getSeconds() == 0 ? "s" : "") +
|
||||||
|
" ago";
|
||||||
|
|
||||||
return std::string(buf);
|
return std::string(buf);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Utils::Time::timeToString(mTime, fmt);
|
return Utils::Time::timeToString(mTime, fmt);
|
||||||
|
@ -296,16 +276,16 @@ void DateTimeEditComponent::updateTextCache()
|
||||||
dispString = "";
|
dispString = "";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) :
|
dispString =
|
||||||
getDisplayString(mode);
|
mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode);
|
||||||
}
|
}
|
||||||
std::shared_ptr<Font> font = getFont();
|
std::shared_ptr<Font> font = getFont();
|
||||||
mTextCache = std::unique_ptr<TextCache>(font->buildTextCache(dispString, 0, 0, mColor));
|
mTextCache = std::unique_ptr<TextCache>(font->buildTextCache(dispString, 0, 0, mColor));
|
||||||
|
|
||||||
if (mAutoSize) {
|
if (mAutoSize) {
|
||||||
mSize = mTextCache->metrics.size;
|
mSize = mTextCache->metrics.size;
|
||||||
|
|
||||||
mAutoSize = false;
|
mAutoSize = false;
|
||||||
|
|
||||||
if (getParent())
|
if (getParent())
|
||||||
getParent()->onSizeChanged();
|
getParent()->onSizeChanged();
|
||||||
}
|
}
|
||||||
|
@ -367,7 +347,9 @@ void DateTimeEditComponent::setUppercase(bool uppercase)
|
||||||
}
|
}
|
||||||
|
|
||||||
void DateTimeEditComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void DateTimeEditComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
#define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
|
||||||
|
|
||||||
#include "utils/TimeUtil.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "utils/TimeUtil.h"
|
||||||
|
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
|
||||||
|
@ -18,21 +18,22 @@ class TextCache;
|
||||||
class DateTimeEditComponent : public GuiComponent
|
class DateTimeEditComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
enum DisplayMode{
|
enum DisplayMode {
|
||||||
DISP_DATE,
|
DISP_DATE, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
DISP_DATE_TIME,
|
DISP_DATE_TIME,
|
||||||
DISP_RELATIVE_TO_NOW
|
DISP_RELATIVE_TO_NOW
|
||||||
};
|
};
|
||||||
|
|
||||||
DateTimeEditComponent(Window* window, bool alignRight = false,
|
DateTimeEditComponent(Window* window,
|
||||||
DisplayMode dispMode = DISP_DATE);
|
bool alignRight = false,
|
||||||
|
DisplayMode dispMode = DISP_DATE);
|
||||||
|
|
||||||
void setValue(const std::string& val) override;
|
void setValue(const std::string& val) override;
|
||||||
std::string getValue() const override;
|
std::string getValue() const override { return mTime; }
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
unsigned int getColor() const override { return mColor; };
|
unsigned int getColor() const override { return mColor; }
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
|
@ -45,19 +46,21 @@ public:
|
||||||
// The initial value is DISP_DATE.
|
// The initial value is DISP_DATE.
|
||||||
void setDisplayMode(DisplayMode mode);
|
void setDisplayMode(DisplayMode mode);
|
||||||
|
|
||||||
// Text color.
|
// Text color.
|
||||||
void setColor(unsigned int color) override;
|
void setColor(unsigned int color) override;
|
||||||
// Font to use. Default is Font::get(FONT_SIZE_MEDIUM).
|
// Font to use. Default is Font::get(FONT_SIZE_MEDIUM).
|
||||||
|
|
||||||
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; };
|
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }
|
||||||
void setChangedColor(unsigned int color) override { mColorChangedValue = color; };
|
void setChangedColor(unsigned int color) override { mColorChangedValue = color; }
|
||||||
|
|
||||||
void setFont(std::shared_ptr<Font> font);
|
void setFont(std::shared_ptr<Font> font);
|
||||||
// Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
|
// Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
|
||||||
void setUppercase(bool uppercase);
|
void setUppercase(bool uppercase);
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
|
@ -65,7 +68,7 @@ private:
|
||||||
std::shared_ptr<Font> getFont() const override;
|
std::shared_ptr<Font> getFont() const override;
|
||||||
|
|
||||||
std::string getDisplayString(DisplayMode mode) const;
|
std::string getDisplayString(DisplayMode mode) const;
|
||||||
DisplayMode getCurrentDisplayMode() const;
|
DisplayMode getCurrentDisplayMode() const { return mDisplayMode; }
|
||||||
|
|
||||||
void updateTextCache();
|
void updateTextCache();
|
||||||
|
|
||||||
|
|
|
@ -8,16 +8,17 @@
|
||||||
|
|
||||||
#include "GridTileComponent.h"
|
#include "GridTileComponent.h"
|
||||||
|
|
||||||
|
#include "ThemeData.h"
|
||||||
#include "animations/LambdaAnimation.h"
|
#include "animations/LambdaAnimation.h"
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
#include "ThemeData.h"
|
|
||||||
|
|
||||||
GridTileComponent::GridTileComponent(Window* window) :
|
GridTileComponent::GridTileComponent(Window* window)
|
||||||
GuiComponent(window), mBackground(window, ":/graphics/frame.png")
|
: GuiComponent(window)
|
||||||
|
, mBackground(window, ":/graphics/frame.png")
|
||||||
{
|
{
|
||||||
mDefaultProperties.mSize = getDefaultTileSize();
|
mDefaultProperties.mSize = getDefaultTileSize();
|
||||||
mDefaultProperties.mPadding = Vector2f(16.0f * Renderer::getScreenWidthModifier(),
|
mDefaultProperties.mPadding = Vector2f(16.0f * Renderer::getScreenWidthModifier(),
|
||||||
16.0f * Renderer::getScreenHeightModifier());
|
16.0f * Renderer::getScreenHeightModifier());
|
||||||
mDefaultProperties.mImageColor = 0xAAAAAABB;
|
mDefaultProperties.mImageColor = 0xAAAAAABB;
|
||||||
// Attempting to use frame.svg instead causes quite severe performance problems.
|
// Attempting to use frame.svg instead causes quite severe performance problems.
|
||||||
mDefaultProperties.mBackgroundImage = ":/graphics/frame.png";
|
mDefaultProperties.mBackgroundImage = ":/graphics/frame.png";
|
||||||
|
@ -76,7 +77,7 @@ void GridTileComponent::update(int deltaTime)
|
||||||
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties)
|
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties)
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
if (elem->has("size"))
|
if (elem->has("size"))
|
||||||
properties->mSize = elem->get<Vector2f>("size") * screen;
|
properties->mSize = elem->get<Vector2f>("size") * screen;
|
||||||
|
@ -106,10 +107,12 @@ void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTilePropert
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& /*element*/, unsigned int /*properties*/)
|
const std::string& view,
|
||||||
|
const std::string& /*element*/,
|
||||||
|
unsigned int /*properties*/)
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
// Apply theme to the default gridtile.
|
// Apply theme to the default gridtile.
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, "default", "gridtile");
|
||||||
|
@ -133,26 +136,23 @@ void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
Vector2f GridTileComponent::getDefaultTileSize()
|
Vector2f GridTileComponent::getDefaultTileSize()
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
return screen * 0.22f;
|
return screen * 0.22f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f GridTileComponent::getSelectedTileSize() const
|
Vector2f GridTileComponent::getSelectedTileSize() const
|
||||||
{
|
{
|
||||||
|
// Return the tile size.
|
||||||
return mDefaultProperties.mSize * 1.2f;
|
return mDefaultProperties.mSize * 1.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GridTileComponent::isSelected() const
|
bool GridTileComponent::isSelected() const
|
||||||
{
|
{
|
||||||
|
// Return whether the tile is selected.
|
||||||
return mSelected;
|
return mSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::reset()
|
|
||||||
{
|
|
||||||
setImage("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void GridTileComponent::setImage(const std::string& path)
|
void GridTileComponent::setImage(const std::string& path)
|
||||||
{
|
{
|
||||||
mImage->setImage(path);
|
mImage->setImage(path);
|
||||||
|
@ -169,8 +169,10 @@ void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setSelected(
|
void GridTileComponent::setSelected(bool selected,
|
||||||
bool selected, bool allowAnimation, Vector3f* pPosition, bool force)
|
bool allowAnimation,
|
||||||
|
Vector3f* pPosition,
|
||||||
|
bool force)
|
||||||
{
|
{
|
||||||
if (mSelected == selected && !force)
|
if (mSelected == selected && !force)
|
||||||
return;
|
return;
|
||||||
|
@ -191,15 +193,18 @@ void GridTileComponent::setSelected(
|
||||||
|
|
||||||
auto func = [this](float t) {
|
auto func = [this](float t) {
|
||||||
t -= 1; // Cubic ease out.
|
t -= 1; // Cubic ease out.
|
||||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
float pct = Math::lerp(0, 1, t * t * t + 1);
|
||||||
this->setSelectedZoom(pct);
|
this->setSelectedZoom(pct);
|
||||||
};
|
};
|
||||||
|
|
||||||
cancelAnimation(3);
|
cancelAnimation(3);
|
||||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
setAnimation(
|
||||||
this->setSelectedZoom(1);
|
new LambdaAnimation(func, 250), 0,
|
||||||
mAnimPosition = Vector3f(0, 0, 0);
|
[this] {
|
||||||
}, false, 3);
|
this->setSelectedZoom(1);
|
||||||
|
mAnimPosition = Vector3f(0, 0, 0);
|
||||||
|
},
|
||||||
|
false, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -213,15 +218,14 @@ void GridTileComponent::setSelected(
|
||||||
this->setSelectedZoom(1);
|
this->setSelectedZoom(1);
|
||||||
|
|
||||||
auto func = [this](float t) {
|
auto func = [this](float t) {
|
||||||
t -= 1; // Cubic ease out.
|
t -= 1.0f; // Cubic ease out.
|
||||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
float pct = Math::lerp(0, 1, t * t * t + 1.0f);
|
||||||
this->setSelectedZoom(1.0f - pct);
|
this->setSelectedZoom(1.0f - pct);
|
||||||
};
|
};
|
||||||
|
|
||||||
cancelAnimation(3);
|
cancelAnimation(3);
|
||||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
setAnimation(
|
||||||
this->setSelectedZoom(0);
|
new LambdaAnimation(func, 250), 0, [this] { this->setSelectedZoom(0); }, false, 3);
|
||||||
}, false, 3);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,10 +239,7 @@ void GridTileComponent::setSelectedZoom(float percent)
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setVisible(bool visible)
|
void GridTileComponent::setVisible(bool visible) { mVisible = visible; }
|
||||||
{
|
|
||||||
mVisible = visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GridTileComponent::resize()
|
void GridTileComponent::resize()
|
||||||
{
|
{
|
||||||
|
@ -278,30 +279,31 @@ void GridTileComponent::calcCurrentProperties()
|
||||||
if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) {
|
if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) {
|
||||||
if (mDefaultProperties.mSize != mSelectedProperties.mSize)
|
if (mDefaultProperties.mSize != mSelectedProperties.mSize)
|
||||||
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse +
|
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse +
|
||||||
mSelectedProperties.mSize * mSelectedZoomPercent;
|
mSelectedProperties.mSize * mSelectedZoomPercent;
|
||||||
|
|
||||||
if (mDefaultProperties.mPadding != mSelectedProperties.mPadding)
|
if (mDefaultProperties.mPadding != mSelectedProperties.mPadding)
|
||||||
mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse +
|
mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse +
|
||||||
mSelectedProperties.mPadding * mSelectedZoomPercent;
|
mSelectedProperties.mPadding * mSelectedZoomPercent;
|
||||||
|
|
||||||
if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor)
|
if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor)
|
||||||
mCurrentProperties.mImageColor = mixColors(mDefaultProperties.mImageColor,
|
mCurrentProperties.mImageColor =
|
||||||
mSelectedProperties.mImageColor, mSelectedZoomPercent);
|
mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor,
|
||||||
|
mSelectedZoomPercent);
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
|
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
|
||||||
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize *
|
mCurrentProperties.mBackgroundCornerSize =
|
||||||
zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize *
|
mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse +
|
||||||
mSelectedZoomPercent;
|
mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent;
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
|
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
|
||||||
mCurrentProperties.mBackgroundCenterColor =
|
mCurrentProperties.mBackgroundCenterColor =
|
||||||
mixColors(mDefaultProperties.mBackgroundCenterColor,
|
mixColors(mDefaultProperties.mBackgroundCenterColor,
|
||||||
mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
|
mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
|
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
|
||||||
mCurrentProperties.mBackgroundEdgeColor =
|
mCurrentProperties.mBackgroundEdgeColor =
|
||||||
mixColors(mDefaultProperties.mBackgroundEdgeColor,
|
mixColors(mDefaultProperties.mBackgroundEdgeColor,
|
||||||
mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
|
mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,8 +28,10 @@ public:
|
||||||
GridTileComponent(Window* window);
|
GridTileComponent(Window* window);
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
// Made this a static function because the ImageGridComponent needs to know the default tile
|
// Made this a static function because the ImageGridComponent needs to know the default tile
|
||||||
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
|
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
|
||||||
|
@ -37,12 +39,14 @@ public:
|
||||||
Vector2f getSelectedTileSize() const;
|
Vector2f getSelectedTileSize() const;
|
||||||
bool isSelected() const;
|
bool isSelected() const;
|
||||||
|
|
||||||
void reset();
|
void reset() { setImage(""); }
|
||||||
|
|
||||||
void setImage(const std::string& path);
|
void setImage(const std::string& path);
|
||||||
void setImage(const std::shared_ptr<TextureResource>& texture);
|
void setImage(const std::shared_ptr<TextureResource>& texture);
|
||||||
void setSelected(bool selected, bool allowAnimation = true,
|
void setSelected(bool selected,
|
||||||
Vector3f* pPosition = nullptr, bool force=false);
|
bool allowAnimation = true,
|
||||||
|
Vector3f* pPosition = nullptr,
|
||||||
|
bool force = false);
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
|
|
||||||
void forceSize(Vector2f size, float selectedZoom);
|
void forceSize(Vector2f size, float selectedZoom);
|
||||||
|
|
|
@ -8,20 +8,21 @@
|
||||||
|
|
||||||
#include "components/HelpComponent.h"
|
#include "components/HelpComponent.h"
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Settings.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Log.h"
|
|
||||||
#include "Settings.h"
|
|
||||||
|
|
||||||
#define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px).
|
#define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px).
|
||||||
#define ENTRY_SPACING 16 // Space between [text] and next [icon] (px).
|
#define ENTRY_SPACING 16 // Space between [text] and next [icon] (px).
|
||||||
|
|
||||||
static std::map<std::string, std::string> sIconPathMap {};
|
static std::map<std::string, std::string> sIconPathMap {};
|
||||||
|
|
||||||
HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
|
HelpComponent::HelpComponent(Window* window)
|
||||||
|
: GuiComponent(window)
|
||||||
{
|
{
|
||||||
assignIcons();
|
assignIcons();
|
||||||
}
|
}
|
||||||
|
@ -113,7 +114,7 @@ void HelpComponent::updateGrid()
|
||||||
std::shared_ptr<Font>& font = mStyle.font;
|
std::shared_ptr<Font>& font = mStyle.font;
|
||||||
|
|
||||||
mGrid = std::make_shared<ComponentGrid>(mWindow,
|
mGrid = std::make_shared<ComponentGrid>(mWindow,
|
||||||
Vector2i(static_cast<int>(mPrompts.size()) * 4, 1));
|
Vector2i(static_cast<int>(mPrompts.size()) * 4, 1));
|
||||||
|
|
||||||
// [icon] [spacer1] [text] [spacer2]
|
// [icon] [spacer1] [text] [spacer2]
|
||||||
|
|
||||||
|
@ -130,12 +131,12 @@ void HelpComponent::updateGrid()
|
||||||
icon->setResize(0, height);
|
icon->setResize(0, height);
|
||||||
icons.push_back(icon);
|
icons.push_back(icon);
|
||||||
|
|
||||||
auto lbl = std::make_shared<TextComponent>(mWindow,
|
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(it->second),
|
||||||
Utils::String::toUpper(it->second), font, mStyle.textColor);
|
font, mStyle.textColor);
|
||||||
labels.push_back(lbl);
|
labels.push_back(lbl);
|
||||||
|
|
||||||
width += icon->getSize().x() + lbl->getSize().x() +
|
width += icon->getSize().x() + lbl->getSize().x() +
|
||||||
((ICON_TEXT_SPACING + ENTRY_SPACING) * Renderer::getScreenWidthModifier());
|
((ICON_TEXT_SPACING + ENTRY_SPACING) * Renderer::getScreenWidthModifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
mGrid->setSize(width, height);
|
mGrid->setSize(width, height);
|
||||||
|
@ -143,8 +144,8 @@ void HelpComponent::updateGrid()
|
||||||
for (unsigned int i = 0; i < icons.size(); i++) {
|
for (unsigned int i = 0; i < icons.size(); i++) {
|
||||||
const int col = i * 4;
|
const int col = i * 4;
|
||||||
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width);
|
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width);
|
||||||
mGrid->setColWidthPerc(col + 1, (ICON_TEXT_SPACING *
|
mGrid->setColWidthPerc(col + 1,
|
||||||
Renderer::getScreenWidthModifier()) / width);
|
(ICON_TEXT_SPACING * Renderer::getScreenWidthModifier()) / width);
|
||||||
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width);
|
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width);
|
||||||
|
|
||||||
mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false);
|
mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false);
|
||||||
|
@ -167,13 +168,13 @@ std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
|
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
|
||||||
LOG(LogError) << "Couldn't load help icon \"" << name <<
|
LOG(LogError) << "Couldn't load help icon \"" << name << "\" as the file \""
|
||||||
"\" as the file \"" << pathLookup->second << "\" is missing";
|
<< pathLookup->second << "\" is missing";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> tex =
|
std::shared_ptr<TextureResource> tex =
|
||||||
TextureResource::get(pathLookup->second, false, false, false);
|
TextureResource::get(pathLookup->second, false, false, false);
|
||||||
mIconCache[std::string(name)] = tex;
|
mIconCache[std::string(name)] = tex;
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,17 +9,17 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
||||||
#define ES_CORE_COMPONENTS_ILIST_H
|
#define ES_CORE_COMPONENTS_ILIST_H
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
enum CursorState {
|
enum CursorState {
|
||||||
CURSOR_STOPPED,
|
CURSOR_STOPPED, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
CURSOR_SCROLLING
|
CURSOR_SCROLLING
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ListLoopType {
|
enum ListLoopType {
|
||||||
LIST_ALWAYS_LOOP,
|
LIST_ALWAYS_LOOP, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
LIST_PAUSE_AT_END,
|
LIST_PAUSE_AT_END,
|
||||||
LIST_NEVER_LOOP
|
LIST_NEVER_LOOP
|
||||||
};
|
};
|
||||||
|
@ -35,10 +35,11 @@ struct ScrollTierList {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Default scroll tiers.
|
// Default scroll tiers.
|
||||||
|
// clang-format off
|
||||||
const ScrollTier QUICK_SCROLL_TIERS[] = {
|
const ScrollTier QUICK_SCROLL_TIERS[] = {
|
||||||
{500, 500},
|
{ 500, 500 },
|
||||||
{1200, 114},
|
{ 1200, 114 },
|
||||||
{0, 16}
|
{ 0, 16 }
|
||||||
};
|
};
|
||||||
const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
|
const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
|
||||||
3,
|
3,
|
||||||
|
@ -46,17 +47,17 @@ const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScrollTier SLOW_SCROLL_TIERS[] = {
|
const ScrollTier SLOW_SCROLL_TIERS[] = {
|
||||||
{500, 500},
|
{ 500, 500 },
|
||||||
{0, 200}
|
{ 0, 200 }
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScrollTierList LIST_SCROLL_STYLE_SLOW = {
|
const ScrollTierList LIST_SCROLL_STYLE_SLOW = {
|
||||||
2,
|
2,
|
||||||
SLOW_SCROLL_TIERS
|
SLOW_SCROLL_TIERS
|
||||||
};
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
template <typename EntryData, typename UserData>
|
template <typename EntryData, typename UserData> class IList : public GuiComponent
|
||||||
class IList : public GuiComponent
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Entry {
|
struct Entry {
|
||||||
|
@ -82,14 +83,13 @@ protected:
|
||||||
Window* mWindow;
|
Window* mWindow;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IList(
|
IList(Window* window,
|
||||||
Window* window,
|
const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK,
|
||||||
const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK,
|
const ListLoopType& loopType = LIST_PAUSE_AT_END)
|
||||||
const ListLoopType& loopType = LIST_PAUSE_AT_END)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mTierList(tierList)
|
||||||
mTierList(tierList),
|
, mLoopType(loopType)
|
||||||
mLoopType(loopType),
|
, mWindow(window)
|
||||||
mWindow(window)
|
|
||||||
{
|
{
|
||||||
mCursor = 0;
|
mCursor = 0;
|
||||||
mScrollTier = 0;
|
mScrollTier = 0;
|
||||||
|
@ -101,15 +101,9 @@ public:
|
||||||
mTitleOverlayColor = 0xFFFFFF00;
|
mTitleOverlayColor = 0xFFFFFF00;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isScrolling() const
|
bool isScrolling() const { return (mScrollVelocity != 0 && mScrollTier > 0); }
|
||||||
{
|
|
||||||
return (mScrollVelocity != 0 && mScrollTier > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int getScrollingVelocity()
|
int getScrollingVelocity() { return mScrollVelocity; }
|
||||||
{
|
|
||||||
return mScrollVelocity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stopScrolling()
|
void stopScrolling()
|
||||||
{
|
{
|
||||||
|
@ -128,43 +122,43 @@ public:
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const std::string& getSelectedName()
|
const std::string& getSelectedName()
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.at(mCursor).name;
|
return mEntries.at(mCursor).name;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getSelected() const
|
const UserData& getSelected() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.at(mCursor).object;
|
return mEntries.at(mCursor).object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getNext() const
|
const UserData& getNext() const
|
||||||
{
|
{
|
||||||
// If there is a next entry, then return it, otherwise return the current entry.
|
// If there is a next entry, then return it, otherwise return the current entry.
|
||||||
if (mCursor + 1 < mEntries.size())
|
if (mCursor + 1 < mEntries.size())
|
||||||
return mEntries.at(mCursor+1).object;
|
return mEntries.at(mCursor + 1).object;
|
||||||
else
|
else
|
||||||
return mEntries.at(mCursor).object;
|
return mEntries.at(mCursor).object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getPrevious() const
|
const UserData& getPrevious() const
|
||||||
{
|
{
|
||||||
// If there is a previous entry, then return it, otherwise return the current entry.
|
// If there is a previous entry, then return it, otherwise return the current entry.
|
||||||
if (mCursor != 0)
|
if (mCursor != 0)
|
||||||
return mEntries.at(mCursor-1).object;
|
return mEntries.at(mCursor - 1).object;
|
||||||
else
|
else
|
||||||
return mEntries.at(mCursor).object;
|
return mEntries.at(mCursor).object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getFirst() const
|
const UserData& getFirst() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.front().object;
|
return mEntries.front().object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getLast() const
|
const UserData& getLast() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.back().object;
|
return mEntries.back().object;
|
||||||
|
@ -192,10 +186,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entry management.
|
// Entry management.
|
||||||
void add(const Entry& e)
|
void add(const Entry& e) { mEntries.push_back(e); }
|
||||||
{
|
|
||||||
mEntries.push_back(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool remove(const UserData& obj)
|
bool remove(const UserData& obj)
|
||||||
{
|
{
|
||||||
|
@ -209,10 +200,7 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int size() const
|
int size() const { return static_cast<int>(mEntries.size()); }
|
||||||
{
|
|
||||||
return static_cast<int>(mEntries.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void remove(typename std::vector<Entry>::const_iterator& it)
|
void remove(typename std::vector<Entry>::const_iterator& it)
|
||||||
|
@ -241,7 +229,6 @@ protected:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool listInput(int velocity) // A velocity of 0 = stop scrolling.
|
bool listInput(int velocity) // A velocity of 0 = stop scrolling.
|
||||||
{
|
{
|
||||||
mScrollVelocity = velocity;
|
mScrollVelocity = velocity;
|
||||||
|
@ -284,8 +271,8 @@ protected:
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should we go to the next scrolling tier?
|
// Should we go to the next scrolling tier?
|
||||||
while (mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >=
|
while (mScrollTier < mTierList.count - 1 &&
|
||||||
mTierList.tiers[mScrollTier].length) {
|
mScrollTierAccumulator >= mTierList.tiers[mScrollTier].length) {
|
||||||
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
|
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
|
||||||
mScrollTier++;
|
mScrollTier++;
|
||||||
}
|
}
|
||||||
|
@ -314,11 +301,11 @@ protected:
|
||||||
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
favoritesSorting = Settings::getInstance()->getBool("FavoritesFirst");
|
||||||
|
|
||||||
if (favoritesSorting && getSelected()->getFavorite()) {
|
if (favoritesSorting && getSelected()->getFavorite()) {
|
||||||
#if defined(_MSC_VER) // MSVC compiler.
|
#if defined(_MSC_VER) // MSVC compiler.
|
||||||
titleIndex = Utils::String::wideStringToString(L"\uF005");
|
titleIndex = Utils::String::wideStringToString(L"\uF005");
|
||||||
#else
|
#else
|
||||||
titleIndex = "\uF005";
|
titleIndex = "\uF005";
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
titleIndex = getSelected()->getName();
|
titleIndex = getSelected()->getName();
|
||||||
|
|
|
@ -8,11 +8,11 @@
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "utils/CImgUtil.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
#include "utils/CImgUtil.h"
|
||||||
|
|
||||||
Vector2i ImageComponent::getTextureSize() const
|
Vector2i ImageComponent::getTextureSize() const
|
||||||
{
|
{
|
||||||
|
@ -27,34 +27,27 @@ Vector2f ImageComponent::getSize() const
|
||||||
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
|
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageComponent::ImageComponent(
|
ImageComponent::ImageComponent(Window* window, bool forceLoad, bool dynamic)
|
||||||
Window* window,
|
: GuiComponent(window)
|
||||||
bool forceLoad,
|
, mTargetIsMax(false)
|
||||||
bool dynamic)
|
, mTargetIsMin(false)
|
||||||
: GuiComponent(window),
|
, mFlipX(false)
|
||||||
mTargetIsMax(false),
|
, mFlipY(false)
|
||||||
mTargetIsMin(false),
|
, mTargetSize(0, 0)
|
||||||
mFlipX(false),
|
, mColorShift(0xFFFFFFFF)
|
||||||
mFlipY(false),
|
, mColorShiftEnd(0xFFFFFFFF)
|
||||||
mTargetSize(0, 0),
|
, mColorGradientHorizontal(true)
|
||||||
mColorShift(0xFFFFFFFF),
|
, mForceLoad(forceLoad)
|
||||||
mColorShiftEnd(0xFFFFFFFF),
|
, mDynamic(dynamic)
|
||||||
mColorGradientHorizontal(true),
|
, mFadeOpacity(0)
|
||||||
mForceLoad(forceLoad),
|
, mFading(false)
|
||||||
mDynamic(dynamic),
|
, mRotateByTargetSize(false)
|
||||||
mFadeOpacity(0),
|
, mTopLeftCrop(0.0f, 0.0f)
|
||||||
mFading(false),
|
, mBottomRightCrop(1.0f, 1.0f)
|
||||||
mRotateByTargetSize(false),
|
|
||||||
mTopLeftCrop(0.0f, 0.0f),
|
|
||||||
mBottomRightCrop(1.0f, 1.0f)
|
|
||||||
{
|
{
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageComponent::~ImageComponent()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageComponent::resize()
|
void ImageComponent::resize()
|
||||||
{
|
{
|
||||||
if (!mTexture)
|
if (!mTexture)
|
||||||
|
@ -91,8 +84,8 @@ void ImageComponent::resize()
|
||||||
mSize[1] = floorf(mSize[1] * resizeScale.y());
|
mSize[1] = floorf(mSize[1] * resizeScale.y());
|
||||||
// For SVG rasterization, always calculate width from rounded height (see comment
|
// For SVG rasterization, always calculate width from rounded height (see comment
|
||||||
// above). We need to make sure we're not creating an image larger than max size.
|
// above). We need to make sure we're not creating an image larger than max size.
|
||||||
mSize[0] = std::min((mSize[1] / textureSize.y()) * textureSize.x(),
|
mSize[0] =
|
||||||
mTargetSize.x());
|
std::min((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mTargetIsMin) {
|
else if (mTargetIsMin) {
|
||||||
|
@ -118,7 +111,6 @@ void ImageComponent::resize()
|
||||||
// above). We need to make sure we're not creating an image smaller than min size.
|
// above). We need to make sure we're not creating an image smaller than min size.
|
||||||
mSize[1] = std::max(floorf(mSize[1]), mTargetSize.y());
|
mSize[1] = std::max(floorf(mSize[1]), mTargetSize.y());
|
||||||
mSize[0] = std::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
mSize[0] = std::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If both components are set, we just stretch.
|
// If both components are set, we just stretch.
|
||||||
|
@ -147,16 +139,6 @@ void ImageComponent::resize()
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::onSizeChanged()
|
|
||||||
{
|
|
||||||
updateVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageComponent::setDefaultImage(std::string path)
|
|
||||||
{
|
|
||||||
mDefaultPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageComponent::setImage(std::string path, bool tile)
|
void ImageComponent::setImage(std::string path, bool tile)
|
||||||
{
|
{
|
||||||
// Always load bundled graphic resources statically, unless mForceLoad has been set.
|
// Always load bundled graphic resources statically, unless mForceLoad has been set.
|
||||||
|
@ -218,16 +200,6 @@ void ImageComponent::setMinSize(float width, float height)
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f ImageComponent::getRotationSize() const
|
|
||||||
{
|
|
||||||
return mRotateByTargetSize ? mTargetSize : mSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageComponent::setRotateByTargetSize(bool rotate)
|
|
||||||
{
|
|
||||||
mRotateByTargetSize = rotate;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImageComponent::cropLeft(float percent)
|
void ImageComponent::cropLeft(float percent)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(percent >= 0.0f && percent <= 1.0f);
|
||||||
|
@ -262,6 +234,7 @@ void ImageComponent::crop(float left, float top, float right, float bot)
|
||||||
|
|
||||||
void ImageComponent::uncrop()
|
void ImageComponent::uncrop()
|
||||||
{
|
{
|
||||||
|
// Remove any applied crop.
|
||||||
crop(0, 0, 0, 0);
|
crop(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,10 +348,12 @@ void ImageComponent::updateVertices()
|
||||||
const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f;
|
const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f;
|
||||||
const float py = mTexture->isTiled() ? mSize.y() / getTextureSize().y() : 1.0f;
|
const float py = mTexture->isTiled() ? mSize.y() / getTextureSize().y() : 1.0f;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
mVertices[0] = { { topLeft.x(), topLeft.y() }, { mTopLeftCrop.x(), py - mTopLeftCrop.y() }, 0 };
|
mVertices[0] = { { topLeft.x(), topLeft.y() }, { mTopLeftCrop.x(), py - mTopLeftCrop.y() }, 0 };
|
||||||
mVertices[1] = { { topLeft.x(), bottomRight.y() }, { mTopLeftCrop.x(), 1.0f - mBottomRightCrop.y() }, 0 };
|
mVertices[1] = { { topLeft.x(), bottomRight.y() }, { mTopLeftCrop.x(), 1.0f - mBottomRightCrop.y() }, 0 };
|
||||||
mVertices[2] = { { bottomRight.x(), topLeft.y() }, { mBottomRightCrop.x() * px, py - mTopLeftCrop.y() }, 0 };
|
mVertices[2] = { { bottomRight.x(), topLeft.y() }, { mBottomRightCrop.x() * px, py - mTopLeftCrop.y() }, 0 };
|
||||||
mVertices[3] = { { bottomRight.x(), bottomRight.y() }, { mBottomRightCrop.x() * px, 1.0f - mBottomRightCrop.y() }, 0 };
|
mVertices[3] = { { bottomRight.x(), bottomRight.y() }, { mBottomRightCrop.x() * px, 1.0f - mBottomRightCrop.y() }, 0 };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
updateColors();
|
updateColors();
|
||||||
|
|
||||||
|
@ -400,10 +375,11 @@ void ImageComponent::updateVertices()
|
||||||
void ImageComponent::updateColors()
|
void ImageComponent::updateColors()
|
||||||
{
|
{
|
||||||
const float opacity = (mOpacity * (mFading ? mFadeOpacity / 255.0f : 1.0f)) / 255.0f;
|
const float opacity = (mOpacity * (mFading ? mFadeOpacity / 255.0f : 1.0f)) / 255.0f;
|
||||||
const unsigned int color = Renderer::convertRGBAToABGR((mColorShift & 0xFFFFFF00) |
|
const unsigned int color = Renderer::convertRGBAToABGR(
|
||||||
static_cast<unsigned char>((mColorShift & 0xFF) * opacity));
|
(mColorShift & 0xFFFFFF00) | static_cast<unsigned char>((mColorShift & 0xFF) * opacity));
|
||||||
const unsigned int colorEnd = Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) |
|
const unsigned int colorEnd =
|
||||||
static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity));
|
Renderer::convertRGBAToABGR((mColorShiftEnd & 0xFFFFFF00) |
|
||||||
|
static_cast<unsigned char>((mColorShiftEnd & 0xFF) * opacity));
|
||||||
|
|
||||||
mVertices[0].col = color;
|
mVertices[0].col = color;
|
||||||
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
|
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
|
||||||
|
@ -423,7 +399,7 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
|
||||||
if (Settings::getInstance()->getBool("DebugImage")) {
|
if (Settings::getInstance()->getBool("DebugImage")) {
|
||||||
Vector2f targetSizePos = (mTargetSize - mSize) * mOrigin * -1;
|
Vector2f targetSizePos = (mTargetSize - mSize) * mOrigin * -1;
|
||||||
Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(),
|
Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(),
|
||||||
mTargetSize.y(), 0xFF000033, 0xFF000033);
|
mTargetSize.y(), 0xFF000033, 0xFF000033);
|
||||||
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
|
||||||
}
|
}
|
||||||
// An image with zero size would normally indicate a corrupt image file.
|
// An image with zero size would normally indicate a corrupt image file.
|
||||||
|
@ -433,12 +409,12 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
|
||||||
// texture is bound in this case but we want to handle a fade so it doesn't just
|
// texture is bound in this case but we want to handle a fade so it doesn't just
|
||||||
// 'jump' in when it finally loads.
|
// 'jump' in when it finally loads.
|
||||||
fadeIn(mTexture->bind());
|
fadeIn(mTexture->bind());
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
if (mSaturation < 1.0) {
|
if (mSaturation < 1.0) {
|
||||||
mVertices[0].shaders = Renderer::SHADER_DESATURATE;
|
mVertices[0].shaders = Renderer::SHADER_DESATURATE;
|
||||||
mVertices[0].saturation = mSaturation;
|
mVertices[0].saturation = mSaturation;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
|
Renderer::drawTriangleStrips(&mVertices[0], 4, trans);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -448,8 +424,8 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
|
||||||
else {
|
else {
|
||||||
std::string textureFilePath = mTexture->getTextureFilePath();
|
std::string textureFilePath = mTexture->getTextureFilePath();
|
||||||
if (textureFilePath != "") {
|
if (textureFilePath != "") {
|
||||||
LOG(LogError) << "Image texture for file \"" << textureFilePath <<
|
LOG(LogError) << "Image texture for file \"" << textureFilePath
|
||||||
"\" has zero size";
|
<< "\" has zero size";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(LogError) << "Image texture has zero size";
|
LOG(LogError) << "Image texture has zero size";
|
||||||
|
@ -492,26 +468,24 @@ void ImageComponent::fadeIn(bool textureLoaded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageComponent::hasImage()
|
void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
{
|
const std::string& view,
|
||||||
return (bool)mTexture;
|
const std::string& element,
|
||||||
}
|
unsigned int properties)
|
||||||
|
|
||||||
void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
|
||||||
const std::string& element, unsigned int properties)
|
|
||||||
{
|
{
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
GuiComponent::applyTheme(theme, view, element, (properties ^ ThemeFlags::SIZE) |
|
GuiComponent::applyTheme(theme, view, element,
|
||||||
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
|
(properties ^ ThemeFlags::SIZE) |
|
||||||
|
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "image");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "image");
|
||||||
if (!elem)
|
if (!elem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Vector2f scale = getParent() ? getParent()->getSize() :
|
Vector2f scale = getParent() ? getParent()->getSize() :
|
||||||
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
if (properties & ThemeFlags::SIZE) {
|
if (properties & ThemeFlags::SIZE) {
|
||||||
if (elem->has("size"))
|
if (elem->has("size"))
|
||||||
|
@ -536,8 +510,8 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const s
|
||||||
if (elem->has("colorEnd"))
|
if (elem->has("colorEnd"))
|
||||||
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
||||||
if (elem->has("gradientType"))
|
if (elem->has("gradientType"))
|
||||||
setColorGradientHorizontal(!(elem->
|
setColorGradientHorizontal(
|
||||||
get<std::string>("gradientType").compare("horizontal")));
|
!(elem->get<std::string>("gradientType").compare("horizontal")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
#define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "math/Vector2i.h"
|
#include "math/Vector2i.h"
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class TextureResource;
|
class TextureResource;
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ class ImageComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ImageComponent(Window* window, bool forceLoad = false, bool dynamic = true);
|
ImageComponent(Window* window, bool forceLoad = false, bool dynamic = true);
|
||||||
virtual ~ImageComponent();
|
virtual ~ImageComponent() {}
|
||||||
|
|
||||||
void setDefaultImage(std::string path);
|
void setDefaultImage(std::string path) { mDefaultPath = path; }
|
||||||
|
|
||||||
// Loads the image at the given filepath. Will tile if tile is true (retrieves texture
|
// Loads the image at the given filepath. Will tile if tile is true (retrieves texture
|
||||||
// as tiling, creates vertices accordingly).
|
// as tiling, creates vertices accordingly).
|
||||||
|
@ -31,7 +31,7 @@ public:
|
||||||
// Use an already existing texture.
|
// Use an already existing texture.
|
||||||
void setImage(const std::shared_ptr<TextureResource>& texture);
|
void setImage(const std::shared_ptr<TextureResource>& texture);
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override { updateVertices(); }
|
||||||
|
|
||||||
// Resize the image to fit this size. If one axis is zero, scale that axis to maintain
|
// Resize the image to fit this size. If one axis is zero, scale that axis to maintain
|
||||||
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
||||||
|
@ -39,18 +39,18 @@ public:
|
||||||
// Can be set before or after an image is loaded.
|
// Can be set before or after an image is loaded.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
void setResize(float width, float height) override;
|
void setResize(float width, float height) override;
|
||||||
inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
||||||
|
|
||||||
// Resize the image to be as large as possible but fit within a box of this size.
|
// Resize the image to be as large as possible but fit within a box of this size.
|
||||||
// Can be set before or after an image is loaded.
|
// Can be set before or after an image is loaded.
|
||||||
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
||||||
void setMaxSize(float width, float height);
|
void setMaxSize(float width, float height);
|
||||||
inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
|
void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
|
||||||
|
|
||||||
void setMinSize(float width, float height);
|
void setMinSize(float width, float height);
|
||||||
inline void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); }
|
void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); }
|
||||||
|
|
||||||
Vector2f getRotationSize() const override;
|
Vector2f getRotationSize() const override { return mRotateByTargetSize ? mTargetSize : mSize; }
|
||||||
|
|
||||||
// Applied AFTER image positioning and sizing.
|
// Applied AFTER image positioning and sizing.
|
||||||
// cropTop(0.2) will crop 20% of the top of the image.
|
// cropTop(0.2) will crop 20% of the top of the image.
|
||||||
|
@ -70,7 +70,7 @@ public:
|
||||||
void setColorShiftEnd(unsigned int color);
|
void setColorShiftEnd(unsigned int color);
|
||||||
void setColorGradientHorizontal(bool horizontal);
|
void setColorGradientHorizontal(bool horizontal);
|
||||||
|
|
||||||
unsigned int getColorShift() const override { return mColorShift; };
|
unsigned int getColorShift() const override { return mColorShift; }
|
||||||
|
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
void setSaturation(float saturation) override;
|
void setSaturation(float saturation) override;
|
||||||
|
@ -79,7 +79,7 @@ public:
|
||||||
void setFlipY(bool flip); // Mirror on the Y axis.
|
void setFlipY(bool flip); // Mirror on the Y axis.
|
||||||
|
|
||||||
// Flag indicating if rotation should be based on target size vs. actual size.
|
// Flag indicating if rotation should be based on target size vs. actual size.
|
||||||
void setRotateByTargetSize(bool rotate);
|
void setRotateByTargetSize(bool rotate) { mRotateByTargetSize = rotate; }
|
||||||
|
|
||||||
// Returns the size of the current texture, or (0, 0) if none is loaded.
|
// Returns the size of the current texture, or (0, 0) if none is loaded.
|
||||||
// May be different than drawn size (use getSize() for that).
|
// May be different than drawn size (use getSize() for that).
|
||||||
|
@ -87,17 +87,18 @@ public:
|
||||||
|
|
||||||
Vector2f getSize() const override;
|
Vector2f getSize() const override;
|
||||||
|
|
||||||
bool hasImage();
|
bool hasImage() { return static_cast<bool>(mTexture); }
|
||||||
|
std::shared_ptr<TextureResource> getTexture() { return mTexture; }
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> getTexture() { return mTexture; };
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector2f mTargetSize;
|
Vector2f mTargetSize;
|
||||||
|
|
||||||
|
|
|
@ -9,21 +9,21 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
||||||
|
|
||||||
|
#include "GridTileComponent.h"
|
||||||
|
#include "Log.h"
|
||||||
#include "animations/LambdaAnimation.h"
|
#include "animations/LambdaAnimation.h"
|
||||||
#include "components/IList.h"
|
#include "components/IList.h"
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
#include "GridTileComponent.h"
|
|
||||||
#include "Log.h"
|
|
||||||
|
|
||||||
#define EXTRAITEMS 2
|
#define EXTRAITEMS 2
|
||||||
|
|
||||||
enum ScrollDirection {
|
enum ScrollDirection {
|
||||||
SCROLL_VERTICALLY,
|
SCROLL_VERTICALLY, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
SCROLL_HORIZONTALLY
|
SCROLL_HORIZONTALLY
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ImageSource {
|
enum ImageSource {
|
||||||
THUMBNAIL,
|
THUMBNAIL, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
IMAGE,
|
IMAGE,
|
||||||
MIXIMAGE,
|
MIXIMAGE,
|
||||||
SCREENSHOT,
|
SCREENSHOT,
|
||||||
|
@ -36,8 +36,7 @@ struct ImageGridData {
|
||||||
std::string texturePath;
|
std::string texturePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> class ImageGridComponent : public IList<ImageGridData, T>
|
||||||
class ImageGridComponent : public IList<ImageGridData, T>
|
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
using IList<ImageGridData, T>::mEntries;
|
using IList<ImageGridData, T>::mEntries;
|
||||||
|
@ -49,9 +48,6 @@ protected:
|
||||||
using IList<ImageGridData, T>::mSize;
|
using IList<ImageGridData, T>::mSize;
|
||||||
using IList<ImageGridData, T>::mCursor;
|
using IList<ImageGridData, T>::mCursor;
|
||||||
using IList<ImageGridData, T>::mWindow;
|
using IList<ImageGridData, T>::mWindow;
|
||||||
// The following change is required for compilation with Clang.
|
|
||||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070
|
|
||||||
// using IList<ImageGridData, T>::Entry;
|
|
||||||
using IList<ImageGridData, T>::IList;
|
using IList<ImageGridData, T>::IList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -66,14 +62,18 @@ public:
|
||||||
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;
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
||||||
{ mCursorChangedCallback = func; }
|
{
|
||||||
|
mCursorChangedCallback = func;
|
||||||
|
}
|
||||||
|
|
||||||
ImageSource getImageSource() { return mImageSource; };
|
ImageSource getImageSource() { return mImageSource; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onCursorChanged(const CursorState& state) override;
|
virtual void onCursorChanged(const CursorState& state) override;
|
||||||
|
@ -81,13 +81,14 @@ protected:
|
||||||
private:
|
private:
|
||||||
// Tiles.
|
// Tiles.
|
||||||
void buildTiles();
|
void buildTiles();
|
||||||
void updateTiles(bool ascending = true, bool allowAnimation = true,
|
void updateTiles(bool ascending = true,
|
||||||
bool updateSelectedState = true);
|
bool allowAnimation = true,
|
||||||
|
bool updateSelectedState = true);
|
||||||
void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState);
|
void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState);
|
||||||
void calcGridDimension();
|
void calcGridDimension();
|
||||||
bool isScrollLoop();
|
bool isScrollLoop();
|
||||||
|
|
||||||
bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; };
|
bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; }
|
||||||
|
|
||||||
// Images and entries.
|
// Images and entries.
|
||||||
bool mEntriesDirty;
|
bool mEntriesDirty;
|
||||||
|
@ -121,11 +122,12 @@ private:
|
||||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData, T>(window)
|
ImageGridComponent<T>::ImageGridComponent(Window* window)
|
||||||
|
: IList<ImageGridData, T>(window)
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
mCamera = 0.0;
|
mCamera = 0.0;
|
||||||
mCameraDirection = 1.0;
|
mCameraDirection = 1.0;
|
||||||
|
@ -152,7 +154,7 @@ ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData,
|
||||||
mImageSource = THUMBNAIL;
|
mImageSource = THUMBNAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
void ImageGridComponent<T>::add(const std::string& name, const std::string& imagePath, const T& obj)
|
void ImageGridComponent<T>::add(const std::string& name, const std::string& imagePath, const T& obj)
|
||||||
{
|
{
|
||||||
typename IList<ImageGridData, T>::Entry entry;
|
typename IList<ImageGridData, T>::Entry entry;
|
||||||
|
@ -164,8 +166,7 @@ void ImageGridComponent<T>::add(const std::string& name, const std::string& imag
|
||||||
mEntriesDirty = true;
|
mEntriesDirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
||||||
bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
|
||||||
{
|
{
|
||||||
if (input.value != 0) {
|
if (input.value != 0) {
|
||||||
int idx = isVertical() ? 0 : 1;
|
int idx = isVertical() ? 0 : 1;
|
||||||
|
@ -190,15 +191,15 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
|
||||||
config->isMappedLike("left", input) || config->isMappedLike("right", input))
|
config->isMappedLike("left", input) || config->isMappedLike("right", input)) {
|
||||||
stopScrolling();
|
stopScrolling();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::update(int deltaTime)
|
||||||
void ImageGridComponent<T>::update(int deltaTime)
|
|
||||||
{
|
{
|
||||||
GuiComponent::update(deltaTime);
|
GuiComponent::update(deltaTime);
|
||||||
listUpdate(deltaTime);
|
listUpdate(deltaTime);
|
||||||
|
@ -207,16 +208,15 @@ void ImageGridComponent<T>::update(int deltaTime)
|
||||||
(*it)->update(deltaTime);
|
(*it)->update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
|
||||||
{
|
{
|
||||||
Transform4x4f trans = getTransform() * parentTrans;
|
Transform4x4f trans = getTransform() * parentTrans;
|
||||||
Transform4x4f tileTrans = trans;
|
Transform4x4f tileTrans = trans;
|
||||||
|
|
||||||
float offsetX = isVertical() ? 0.0f : mCamera * mCameraDirection *
|
float offsetX =
|
||||||
(mTileSize.x() + mMargin.x());
|
isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x() + mMargin.x());
|
||||||
float offsetY = isVertical() ? mCamera * mCameraDirection *
|
float offsetY =
|
||||||
(mTileSize.y() + mMargin.y()) : 0.0f;
|
isVertical() ? mCamera * mCameraDirection * (mTileSize.y() + mMargin.y()) : 0.0f;
|
||||||
|
|
||||||
tileTrans.translate(Vector3f(offsetX, offsetY, 0.0));
|
tileTrans.translate(Vector3f(offsetX, offsetY, 0.0));
|
||||||
|
|
||||||
|
@ -230,9 +230,9 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
float scaleY = trans.r1().y();
|
float scaleY = trans.r1().y();
|
||||||
|
|
||||||
Vector2i pos(static_cast<int>(std::round(trans.translation()[0])),
|
Vector2i pos(static_cast<int>(std::round(trans.translation()[0])),
|
||||||
static_cast<int>(std::round(trans.translation()[1])));
|
static_cast<int>(std::round(trans.translation()[1])));
|
||||||
Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)),
|
Vector2i size(static_cast<int>(std::round(mSize.x() * scaleX)),
|
||||||
static_cast<int>(std::round(mSize.y() * scaleY)));
|
static_cast<int>(std::round(mSize.y() * scaleY)));
|
||||||
|
|
||||||
Renderer::pushClipRect(pos, size);
|
Renderer::pushClipRect(pos, size);
|
||||||
|
|
||||||
|
@ -258,9 +258,11 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
// Apply theme to GuiComponent but not the size property, which will be applied
|
// Apply theme to GuiComponent but not the size property, which will be applied
|
||||||
// at the end of this function.
|
// at the end of this function.
|
||||||
|
@ -270,7 +272,7 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
mTheme = theme;
|
mTheme = theme;
|
||||||
|
|
||||||
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f screen = Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid");
|
||||||
if (elem) {
|
if (elem) {
|
||||||
|
@ -279,7 +281,7 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
|
|
||||||
if (elem->has("padding"))
|
if (elem->has("padding"))
|
||||||
mPadding = elem->get<Vector4f>("padding") *
|
mPadding = elem->get<Vector4f>("padding") *
|
||||||
Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
|
Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
|
||||||
|
|
||||||
if (elem->has("autoLayout"))
|
if (elem->has("autoLayout"))
|
||||||
mAutoLayout = elem->get<Vector2f>("autoLayout");
|
mAutoLayout = elem->get<Vector2f>("autoLayout");
|
||||||
|
@ -309,8 +311,8 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem->has("scrollDirection"))
|
if (elem->has("scrollDirection"))
|
||||||
mScrollDirection = (ScrollDirection)(elem->
|
mScrollDirection =
|
||||||
get<std::string>("scrollDirection") == "horizontal");
|
(ScrollDirection)(elem->get<std::string>("scrollDirection") == "horizontal");
|
||||||
|
|
||||||
if (elem->has("centerSelection")) {
|
if (elem->has("centerSelection")) {
|
||||||
mCenterSelection = (elem->get<bool>("centerSelection"));
|
mCenterSelection = (elem->get<bool>("centerSelection"));
|
||||||
|
@ -367,9 +369,8 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
// grid dimension, and then (re)build the tiles.
|
// grid dimension, and then (re)build the tiles.
|
||||||
elem = theme->getElement(view, "default", "gridtile");
|
elem = theme->getElement(view, "default", "gridtile");
|
||||||
|
|
||||||
mTileSize = elem && elem->has("size") ?
|
mTileSize = elem && elem->has("size") ? elem->get<Vector2f>("size") * screen :
|
||||||
elem->get<Vector2f>("size") * screen :
|
GridTileComponent::getDefaultTileSize();
|
||||||
GridTileComponent::getDefaultTileSize();
|
|
||||||
|
|
||||||
// Apply size property which will trigger a call to onSizeChanged() which will build the tiles.
|
// Apply size property which will trigger a call to onSizeChanged() which will build the tiles.
|
||||||
GuiComponent::applyTheme(theme, view, element, ThemeFlags::SIZE);
|
GuiComponent::applyTheme(theme, view, element, ThemeFlags::SIZE);
|
||||||
|
@ -379,15 +380,13 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
buildTiles();
|
buildTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::onSizeChanged()
|
||||||
void ImageGridComponent<T>::onSizeChanged()
|
|
||||||
{
|
{
|
||||||
buildTiles();
|
buildTiles();
|
||||||
updateTiles();
|
updateTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
|
||||||
{
|
{
|
||||||
if (mLastCursor == mCursor) {
|
if (mLastCursor == mCursor) {
|
||||||
if (state == CURSOR_STOPPED && mCursorChangedCallback)
|
if (state == CURSOR_STOPPED && mCursorChangedCallback)
|
||||||
|
@ -470,7 +469,7 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
mStartPosition = lastScroll * dimOpposite;
|
mStartPosition = lastScroll * dimOpposite;
|
||||||
}
|
}
|
||||||
else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) ||
|
else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) ||
|
||||||
col == firstVisibleCol + centralCol) {
|
col == firstVisibleCol + centralCol) {
|
||||||
if (col == firstVisibleCol + maxCentralCol)
|
if (col == firstVisibleCol + maxCentralCol)
|
||||||
mStartPosition = (col - maxCentralCol) * dimOpposite;
|
mStartPosition = (col - maxCentralCol) * dimOpposite;
|
||||||
else
|
else
|
||||||
|
@ -507,23 +506,23 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
if (!moveCamera)
|
if (!moveCamera)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
t -= 1; // Cubic ease out.
|
t -= 1.0f; // Cubic ease out.
|
||||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
float pct = Math::lerp(0, 1.0f, t * t * t + 1.0f);
|
||||||
t = startPos * (1.0f - pct) + endPos * pct;
|
t = startPos * (1.0f - pct) + endPos * pct;
|
||||||
mCamera = t;
|
mCamera = t;
|
||||||
};
|
};
|
||||||
|
|
||||||
reinterpret_cast<GuiComponent*>(this)->setAnimation(
|
reinterpret_cast<GuiComponent*>(this)->setAnimation(
|
||||||
new LambdaAnimation(func, 250), 0, [this, direction] {
|
new LambdaAnimation(func, 250), 0,
|
||||||
mCamera = 0;
|
[this, direction] {
|
||||||
updateTiles(direction, false);
|
mCamera = 0;
|
||||||
}, false, 2);
|
updateTiles(direction, false);
|
||||||
|
},
|
||||||
|
false, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create and position tiles (mTiles).
|
// Create and position tiles (mTiles).
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::buildTiles()
|
||||||
void ImageGridComponent<T>::buildTiles()
|
|
||||||
{
|
{
|
||||||
mStartPosition = 0;
|
mStartPosition = 0;
|
||||||
mTiles.clear();
|
mTiles.clear();
|
||||||
|
@ -531,18 +530,20 @@ void ImageGridComponent<T>::buildTiles()
|
||||||
calcGridDimension();
|
calcGridDimension();
|
||||||
|
|
||||||
if (mCenterSelection) {
|
if (mCenterSelection) {
|
||||||
int dimScrollable = (isVertical() ? mGridDimension.y() :
|
int dimScrollable =
|
||||||
mGridDimension.x()) - 2 * EXTRAITEMS;
|
(isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
|
||||||
mStartPosition -= static_cast<int>(floorf(dimScrollable / 2.0f));
|
mStartPosition -= static_cast<int>(floorf(dimScrollable / 2.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f tileDistance = mTileSize + mMargin;
|
Vector2f tileDistance = mTileSize + mMargin;
|
||||||
|
|
||||||
if (mAutoLayout.x() != 0.0f && mAutoLayout.y() != 0.0f) {
|
if (mAutoLayout.x() != 0.0f && mAutoLayout.y() != 0.0f) {
|
||||||
auto x = (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1.0f)) - mPadding.x() -
|
auto x =
|
||||||
mPadding.z()) / static_cast<int>(mAutoLayout.x());
|
(mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1.0f)) - mPadding.x() - mPadding.z()) /
|
||||||
auto y = (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1.0f)) - mPadding.y() -
|
static_cast<int>(mAutoLayout.x());
|
||||||
mPadding.w()) / static_cast<int>(mAutoLayout.y());
|
auto y =
|
||||||
|
(mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1.0f)) - mPadding.y() - mPadding.w()) /
|
||||||
|
static_cast<int>(mAutoLayout.y());
|
||||||
|
|
||||||
mTileSize = Vector2f(x, y);
|
mTileSize = Vector2f(x, y);
|
||||||
tileDistance = mTileSize + mMargin;
|
tileDistance = mTileSize + mMargin;
|
||||||
|
@ -566,8 +567,8 @@ void ImageGridComponent<T>::buildTiles()
|
||||||
X = vert ? x : y - EXTRAITEMS;
|
X = vert ? x : y - EXTRAITEMS;
|
||||||
Y = vert ? y - EXTRAITEMS : x;
|
Y = vert ? y - EXTRAITEMS : x;
|
||||||
|
|
||||||
tile->setPosition(X * tileDistance.x() + startPosition.x(), Y *
|
tile->setPosition(X * tileDistance.x() + startPosition.x(),
|
||||||
tileDistance.y() + startPosition.y());
|
Y * tileDistance.y() + startPosition.y());
|
||||||
tile->setOrigin(0.5f, 0.5f);
|
tile->setOrigin(0.5f, 0.5f);
|
||||||
tile->setImage("");
|
tile->setImage("");
|
||||||
|
|
||||||
|
@ -582,9 +583,10 @@ void ImageGridComponent<T>::buildTiles()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation,
|
void ImageGridComponent<T>::updateTiles(bool ascending,
|
||||||
bool updateSelectedState)
|
bool allowAnimation,
|
||||||
|
bool updateSelectedState)
|
||||||
{
|
{
|
||||||
if (!mTiles.size())
|
if (!mTiles.size())
|
||||||
return;
|
return;
|
||||||
|
@ -631,9 +633,11 @@ void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation,
|
||||||
mLastCursor = mCursor;
|
mLastCursor = mCursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T>
|
||||||
void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos,
|
void ImageGridComponent<T>::updateTileAtPos(int tilePos,
|
||||||
bool allowAnimation, bool updateSelectedState)
|
int imgPos,
|
||||||
|
bool allowAnimation,
|
||||||
|
bool updateSelectedState)
|
||||||
{
|
{
|
||||||
std::shared_ptr<GridTileComponent> tile = mTiles.at(tilePos);
|
std::shared_ptr<GridTileComponent> tile = mTiles.at(tilePos);
|
||||||
|
|
||||||
|
@ -647,7 +651,7 @@ void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos,
|
||||||
// If we have more tiles than we can display on screen, hide them.
|
// If we have more tiles than we can display on screen, hide them.
|
||||||
// Same for tiles out of the buffer.
|
// Same for tiles out of the buffer.
|
||||||
if (imgPos < 0 || imgPos >= size() || tilePos < 0 ||
|
if (imgPos < 0 || imgPos >= size() || tilePos < 0 ||
|
||||||
tilePos >= static_cast<int>(mTiles.size())) {
|
tilePos >= static_cast<int>(mTiles.size())) {
|
||||||
if (updateSelectedState)
|
if (updateSelectedState)
|
||||||
tile->setSelected(false, allowAnimation);
|
tile->setSelected(false, allowAnimation);
|
||||||
tile->reset();
|
tile->reset();
|
||||||
|
@ -680,14 +684,12 @@ void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos,
|
||||||
tile->setSelected(imgPos == mCursor, allowAnimation);
|
tile->setSelected(imgPos == mCursor, allowAnimation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate how many tiles of size mTileSize we can fit in a grid of size mSize using
|
// Calculate how many tiles of size mTileSize we can fit in a grid of size mSize using
|
||||||
// a margin size of mMargin.
|
// a margin size of mMargin.
|
||||||
template<typename T>
|
template <typename T> void ImageGridComponent<T>::calcGridDimension()
|
||||||
void ImageGridComponent<T>::calcGridDimension()
|
|
||||||
{
|
{
|
||||||
// grid_size = columns * tile_size + (columns - 1) * margin
|
// grid_size = columns * tile_size + (columns - 1) * margin
|
||||||
// <=> columns = (grid_size + margin) / (tile_size + margin)
|
// <=> columns = (grid_size + margin) / (tile_size + margin)
|
||||||
|
@ -700,7 +702,7 @@ void ImageGridComponent<T>::calcGridDimension()
|
||||||
|
|
||||||
// Ceil y dim so we can display partial last row.
|
// Ceil y dim so we can display partial last row.
|
||||||
mGridDimension = Vector2i(static_cast<const int>(gridDimension.x()),
|
mGridDimension = Vector2i(static_cast<const int>(gridDimension.x()),
|
||||||
static_cast<const int>(ceilf(gridDimension.y())));
|
static_cast<const int>(ceilf(gridDimension.y())));
|
||||||
|
|
||||||
// Grid dimension validation.
|
// Grid dimension validation.
|
||||||
if (mGridDimension.x() < 1) {
|
if (mGridDimension.x() < 1) {
|
||||||
|
@ -717,13 +719,13 @@ void ImageGridComponent<T>::calcGridDimension()
|
||||||
mGridDimension.x() += 2 * EXTRAITEMS;
|
mGridDimension.x() += 2 * EXTRAITEMS;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template <typename T> bool ImageGridComponent<T>::isScrollLoop()
|
||||||
bool ImageGridComponent<T>::isScrollLoop() {
|
{
|
||||||
if (!mScrollLoop)
|
if (!mScrollLoop)
|
||||||
return false;
|
return false;
|
||||||
if (isVertical())
|
if (isVertical())
|
||||||
return (mGridDimension.x() * (mGridDimension.y() - 2 * EXTRAITEMS)) <= mEntries.size();
|
return (mGridDimension.x() * (mGridDimension.y() - 2 * EXTRAITEMS)) <= mEntries.size();
|
||||||
return (mGridDimension.y() * (mGridDimension.x() - 2 * EXTRAITEMS)) <= mEntries.size();
|
return (mGridDimension.y() * (mGridDimension.x() - 2 * EXTRAITEMS)) <= mEntries.size();
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
|
||||||
|
|
|
@ -8,22 +8,21 @@
|
||||||
|
|
||||||
#include "components/MenuComponent.h"
|
#include "components/MenuComponent.h"
|
||||||
|
|
||||||
#include "components/ButtonComponent.h"
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "components/ButtonComponent.h"
|
||||||
|
|
||||||
#define BUTTON_GRID_VERT_PADDING 32.0f
|
#define BUTTON_GRID_VERT_PADDING 32.0f
|
||||||
#define BUTTON_GRID_HORIZ_PADDING 10.0f
|
#define BUTTON_GRID_HORIZ_PADDING 10.0f
|
||||||
|
|
||||||
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + TITLE_VERT_PADDING)
|
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + TITLE_VERT_PADDING)
|
||||||
|
|
||||||
MenuComponent::MenuComponent(
|
MenuComponent::MenuComponent(Window* window,
|
||||||
Window* window,
|
std::string title,
|
||||||
std::string title,
|
const std::shared_ptr<Font>& titleFont)
|
||||||
const std::shared_ptr<Font>& titleFont)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mBackground(window)
|
||||||
mBackground(window),
|
, mGrid(window, Vector2i(1, 3))
|
||||||
mGrid(window, Vector2i(1, 3)),
|
, mNeedsSaving(false)
|
||||||
mNeedsSaving(false)
|
|
||||||
{
|
{
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
@ -49,6 +48,7 @@ MenuComponent::MenuComponent(
|
||||||
|
|
||||||
MenuComponent::~MenuComponent()
|
MenuComponent::~MenuComponent()
|
||||||
{
|
{
|
||||||
|
// Save when destroyed.
|
||||||
save();
|
save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,15 +75,15 @@ void MenuComponent::setTitle(std::string title, const std::shared_ptr<Font>& fon
|
||||||
float MenuComponent::getButtonGridHeight() const
|
float MenuComponent::getButtonGridHeight() const
|
||||||
{
|
{
|
||||||
return (mButtonGrid ? mButtonGrid->getSize().y() :
|
return (mButtonGrid ? mButtonGrid->getSize().y() :
|
||||||
Font::get(FONT_SIZE_MEDIUM)->getHeight() +
|
Font::get(FONT_SIZE_MEDIUM)->getHeight() +
|
||||||
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()));
|
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuComponent::updateSize()
|
void MenuComponent::updateSize()
|
||||||
{
|
{
|
||||||
const float maxHeight = Renderer::getScreenHeight() * 0.80f;
|
const float maxHeight = Renderer::getScreenHeight() * 0.80f;
|
||||||
float height = TITLE_HEIGHT + mList->getTotalRowHeight() + getButtonGridHeight() +
|
float height = TITLE_HEIGHT + mList->getTotalRowHeight() + getButtonGridHeight() +
|
||||||
(2.0f * Renderer::getScreenHeightModifier());
|
(2.0f * Renderer::getScreenHeightModifier());
|
||||||
if (height > maxHeight) {
|
if (height > maxHeight) {
|
||||||
height = TITLE_HEIGHT + getButtonGridHeight();
|
height = TITLE_HEIGHT + getButtonGridHeight();
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
@ -98,14 +98,15 @@ void MenuComponent::updateSize()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float width = static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() *
|
float width =
|
||||||
1.05f), static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
|
static_cast<float>(std::min(static_cast<int>(Renderer::getScreenHeight() * 1.05f),
|
||||||
|
static_cast<int>(Renderer::getScreenWidth() * 0.90f)));
|
||||||
setSize(width, height);
|
setSize(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuComponent::onSizeChanged()
|
void MenuComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
|
||||||
// Update grid row/column sizes.
|
// Update grid row/column sizes.
|
||||||
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y());
|
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y());
|
||||||
|
@ -115,10 +116,11 @@ void MenuComponent::onSizeChanged()
|
||||||
}
|
}
|
||||||
|
|
||||||
void MenuComponent::addButton(const std::string& name,
|
void MenuComponent::addButton(const std::string& name,
|
||||||
const std::string& helpText, const std::function<void()>& callback)
|
const std::string& helpText,
|
||||||
|
const std::function<void()>& callback)
|
||||||
{
|
{
|
||||||
mButtons.push_back(std::make_shared<ButtonComponent>
|
mButtons.push_back(std::make_shared<ButtonComponent>(mWindow, Utils::String::toUpper(name),
|
||||||
(mWindow,Utils::String::toUpper(name), helpText, callback));
|
helpText, callback));
|
||||||
updateGrid();
|
updateGrid();
|
||||||
updateSize();
|
updateSize();
|
||||||
}
|
}
|
||||||
|
@ -136,30 +138,28 @@ void MenuComponent::updateGrid()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> MenuComponent::getHelpPrompts()
|
std::shared_ptr<ComponentGrid> makeButtonGrid(
|
||||||
|
Window* window, const std::vector<std::shared_ptr<ButtonComponent>>& buttons)
|
||||||
{
|
{
|
||||||
return mGrid.getHelpPrompts();
|
std::shared_ptr<ComponentGrid> buttonGrid =
|
||||||
}
|
std::make_shared<ComponentGrid>(window, Vector2i(static_cast<int>(buttons.size()), 2));
|
||||||
|
|
||||||
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window,
|
|
||||||
const std::vector<std::shared_ptr<ButtonComponent>>& buttons)
|
|
||||||
{
|
|
||||||
std::shared_ptr<ComponentGrid> buttonGrid = std::make_shared<ComponentGrid>
|
|
||||||
(window, Vector2i(static_cast<int>(buttons.size()), 2));
|
|
||||||
|
|
||||||
// Initialize to padding.
|
// Initialize to padding.
|
||||||
float buttonGridWidth = BUTTON_GRID_HORIZ_PADDING *
|
float buttonGridWidth =
|
||||||
Renderer::getScreenWidthModifier() * buttons.size();
|
BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier() * buttons.size();
|
||||||
for (int i = 0; i < static_cast<int>(buttons.size()); i++) {
|
for (int i = 0; i < static_cast<int>(buttons.size()); i++) {
|
||||||
buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false);
|
buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false);
|
||||||
buttonGridWidth += buttons.at(i)->getSize().x();
|
buttonGridWidth += buttons.at(i)->getSize().x();
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i < buttons.size(); i++)
|
for (unsigned int i = 0; i < buttons.size(); i++)
|
||||||
buttonGrid->setColWidthPerc(i, (buttons.at(i)->getSize().x() +
|
buttonGrid->setColWidthPerc(
|
||||||
BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier()) / buttonGridWidth);
|
i, (buttons.at(i)->getSize().x() +
|
||||||
|
BUTTON_GRID_HORIZ_PADDING * Renderer::getScreenWidthModifier()) /
|
||||||
|
buttonGridWidth);
|
||||||
|
|
||||||
buttonGrid->setSize(buttonGridWidth, buttons.at(0)->getSize().y() +
|
buttonGrid->setSize(buttonGridWidth,
|
||||||
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) + 2);
|
buttons.at(0)->getSize().y() +
|
||||||
|
(BUTTON_GRID_VERT_PADDING * Renderer::getScreenHeightModifier()) + 2);
|
||||||
// Spacer row to deal with dropshadow to make buttons look centered.
|
// Spacer row to deal with dropshadow to make buttons look centered.
|
||||||
buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y());
|
buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y());
|
||||||
|
|
||||||
|
|
|
@ -17,20 +17,21 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
#define TITLE_VERT_PADDING (Renderer::getScreenHeight() * 0.0637f)
|
||||||
|
|
||||||
class ButtonComponent;
|
class ButtonComponent;
|
||||||
class ImageComponent;
|
class ImageComponent;
|
||||||
|
|
||||||
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window,
|
std::shared_ptr<ComponentGrid> makeButtonGrid(
|
||||||
const std::vector<std::shared_ptr<ButtonComponent>>& buttons);
|
Window* window, const std::vector<std::shared_ptr<ButtonComponent>>& buttons);
|
||||||
std::shared_ptr<ImageComponent> makeArrow(Window* window);
|
std::shared_ptr<ImageComponent> makeArrow(Window* window);
|
||||||
|
|
||||||
#define TITLE_VERT_PADDING (Renderer::getScreenHeight() * 0.0637f)
|
|
||||||
|
|
||||||
class MenuComponent : public GuiComponent
|
class MenuComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MenuComponent(Window* window, std::string title,
|
MenuComponent(Window* window,
|
||||||
const std::shared_ptr<Font>& titleFont = Font::get(FONT_SIZE_LARGE));
|
std::string title,
|
||||||
|
const std::shared_ptr<Font>& titleFont = Font::get(FONT_SIZE_LARGE));
|
||||||
virtual ~MenuComponent();
|
virtual ~MenuComponent();
|
||||||
|
|
||||||
void save();
|
void save();
|
||||||
|
@ -38,31 +39,42 @@ public:
|
||||||
|
|
||||||
void setNeedsSaving() { mNeedsSaving = true; }
|
void setNeedsSaving() { mNeedsSaving = true; }
|
||||||
|
|
||||||
inline void addRow(const ComponentListRow& row, bool setCursorHere = false)
|
void addRow(const ComponentListRow& row, bool setCursorHere = false)
|
||||||
{ mList->addRow(row, setCursorHere); updateSize(); }
|
{
|
||||||
|
mList->addRow(row, setCursorHere);
|
||||||
|
updateSize();
|
||||||
|
}
|
||||||
|
|
||||||
inline void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp,
|
void addWithLabel(const std::string& label,
|
||||||
bool setCursorHere = false, bool invert_when_selected = true)
|
const std::shared_ptr<GuiComponent>& comp,
|
||||||
|
bool setCursorHere = false,
|
||||||
|
bool invert_when_selected = true)
|
||||||
{
|
{
|
||||||
ComponentListRow row;
|
ComponentListRow row;
|
||||||
row.addElement(std::make_shared<TextComponent>(mWindow,
|
row.addElement(std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label),
|
||||||
Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF),
|
||||||
|
true);
|
||||||
row.addElement(comp, false, invert_when_selected);
|
row.addElement(comp, false, invert_when_selected);
|
||||||
addRow(row, setCursorHere);
|
addRow(row, setCursorHere);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }
|
void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }
|
||||||
|
|
||||||
void addButton(const std::string& label, const std::string& helpText,
|
void addButton(const std::string& label,
|
||||||
const std::function<void()>& callback);
|
const std::string& helpText,
|
||||||
|
const std::function<void()>& callback);
|
||||||
|
|
||||||
void setTitle(std::string title, const std::shared_ptr<Font>& font);
|
void setTitle(std::string title, const std::shared_ptr<Font>& font);
|
||||||
|
|
||||||
void setCursorToFirstListEntry() { mList->moveCursor(-mList->getCursorId()); }
|
void setCursorToFirstListEntry() { mList->moveCursor(-mList->getCursorId()); }
|
||||||
inline void setCursorToList() { mGrid.setCursorTo(mList); }
|
void setCursorToList() { mGrid.setCursorTo(mList); }
|
||||||
inline void setCursorToButtons() { assert(mButtonGrid); mGrid.setCursorTo(mButtonGrid); }
|
void setCursorToButtons()
|
||||||
|
{
|
||||||
|
assert(mButtonGrid);
|
||||||
|
mGrid.setCursorTo(mButtonGrid);
|
||||||
|
}
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override { return mGrid.getHelpPrompts(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void updateSize();
|
void updateSize();
|
||||||
|
|
|
@ -8,21 +8,20 @@
|
||||||
|
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
|
||||||
NinePatchComponent::NinePatchComponent(
|
NinePatchComponent::NinePatchComponent(Window* window,
|
||||||
Window* window,
|
const std::string& path,
|
||||||
const std::string& path,
|
unsigned int edgeColor,
|
||||||
unsigned int edgeColor,
|
unsigned int centerColor)
|
||||||
unsigned int centerColor)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mCornerSize(16.0f, 16.0f)
|
||||||
mCornerSize(16.0f, 16.0f),
|
, mEdgeColor(edgeColor)
|
||||||
mEdgeColor(edgeColor),
|
, mCenterColor(centerColor)
|
||||||
mCenterColor(centerColor),
|
, mPath(path)
|
||||||
mPath(path),
|
, mVertices(nullptr)
|
||||||
mVertices(nullptr)
|
|
||||||
{
|
{
|
||||||
if (!mPath.empty())
|
if (!mPath.empty())
|
||||||
buildVertices();
|
buildVertices();
|
||||||
|
@ -42,8 +41,8 @@ void NinePatchComponent::updateColors()
|
||||||
for (int i = 0; i < 6 * 9; i++)
|
for (int i = 0; i < 6 * 9; i++)
|
||||||
mVertices[i].col = edgeColor;
|
mVertices[i].col = edgeColor;
|
||||||
|
|
||||||
for (int i = 6*4; i < 6; i++)
|
for (int i = 6 * 4; i < 6; i++)
|
||||||
mVertices[(6*4)+i].col = centerColor;
|
mVertices[(6 * 4) + i].col = centerColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::buildVertices()
|
void NinePatchComponent::buildVertices()
|
||||||
|
@ -72,19 +71,21 @@ void NinePatchComponent::buildVertices()
|
||||||
mVertices = new Renderer::Vertex[6 * 9];
|
mVertices = new Renderer::Vertex[6 * 9];
|
||||||
|
|
||||||
texSize = Vector2f(static_cast<float>(mTexture->getSize().x()),
|
texSize = Vector2f(static_cast<float>(mTexture->getSize().x()),
|
||||||
static_cast<float>(mTexture->getSize().y()));
|
static_cast<float>(mTexture->getSize().y()));
|
||||||
|
|
||||||
const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2, mCornerSize.x()};
|
// clang-format off
|
||||||
const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2, mCornerSize.y()};
|
const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2.0f, mCornerSize.x() };
|
||||||
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]};
|
const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2.0f, mCornerSize.y() };
|
||||||
const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1]};
|
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1] };
|
||||||
|
const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1] };
|
||||||
|
|
||||||
// The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom
|
// The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom
|
||||||
// left corner origin vs. verticies having a top left origin.
|
// left corner origin vs. verticies having a top left origin.
|
||||||
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2) / texSize.x(), mCornerSize.x() / texSize.x() };
|
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2.0f) / texSize.x(), mCornerSize.x() / texSize.x() };
|
||||||
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2) / texSize.y(), -mCornerSize.y() / texSize.y() };
|
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2.0f) / texSize.y(), -mCornerSize.y() / texSize.y() };
|
||||||
const float texPosX[3] = { 0, texSizeX[0], texSizeX[0] + texSizeX[1] };
|
const float texPosX[3] = { 0.0f, texSizeX[0], texSizeX[0] + texSizeX[1] };
|
||||||
const float texPosY[3] = { 1, 1 + texSizeY[0], 1 + texSizeY[0] + texSizeY[1] };
|
const float texPosY[3] = { 1.0f, 1.0f + texSizeY[0], 1.0f + texSizeY[0] + texSizeY[1] };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
int v = 0;
|
int v = 0;
|
||||||
|
|
||||||
|
@ -96,10 +97,12 @@ void NinePatchComponent::buildVertices()
|
||||||
const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]);
|
const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]);
|
||||||
const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]);
|
const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 };
|
mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 };
|
||||||
mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 };
|
mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 };
|
||||||
mVertices[v + 3] = { { imgPos.x() + imgSize.x(), imgPos.y() }, { texPos.x() + texSize.x(), texPos.y() }, 0 };
|
mVertices[v + 3] = { { imgPos.x() + imgSize.x(), imgPos.y() }, { texPos.x() + texSize.x(), texPos.y() }, 0 };
|
||||||
mVertices[v + 4] = { { imgPos.x() + imgSize.x(), imgPos.y() + imgSize.y() }, { texPos.x() + texSize.x(), texPos.y() + texSize.y() }, 0 };
|
mVertices[v + 4] = { { imgPos.x() + imgSize.x(), imgPos.y() + imgSize.y() }, { texPos.x() + texSize.x(), texPos.y() + texSize.y() }, 0 };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Round vertices.
|
// Round vertices.
|
||||||
for (int i = 1; i < 5; i++)
|
for (int i = 1; i < 5; i++)
|
||||||
|
@ -141,10 +144,7 @@ void NinePatchComponent::render(const Transform4x4f& parentTrans)
|
||||||
renderChildren(trans);
|
renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::onSizeChanged()
|
void NinePatchComponent::onSizeChanged() { buildVertices(); }
|
||||||
{
|
|
||||||
buildVertices();
|
|
||||||
}
|
|
||||||
|
|
||||||
void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding)
|
void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding)
|
||||||
{
|
{
|
||||||
|
@ -176,7 +176,9 @@ void NinePatchComponent::setCenterColor(unsigned int centerColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void NinePatchComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
#define ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
class TextureResource;
|
class TextureResource;
|
||||||
|
|
||||||
|
@ -29,16 +29,19 @@ class TextureResource;
|
||||||
class NinePatchComponent : public GuiComponent
|
class NinePatchComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NinePatchComponent(Window* window, const std::string& path = "",
|
NinePatchComponent(Window* window,
|
||||||
unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF);
|
const std::string& path = "",
|
||||||
|
unsigned int edgeColor = 0xFFFFFFFF,
|
||||||
|
unsigned int centerColor = 0xFFFFFFFF);
|
||||||
virtual ~NinePatchComponent();
|
virtual ~NinePatchComponent();
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
void fitTo(Vector2f size, Vector3f position = Vector3f::Zero(),
|
void fitTo(Vector2f size,
|
||||||
Vector2f padding = Vector2f::Zero());
|
Vector3f position = Vector3f::Zero(),
|
||||||
|
Vector2f padding = Vector2f::Zero());
|
||||||
|
|
||||||
void setImagePath(const std::string& path);
|
void setImagePath(const std::string& path);
|
||||||
// Apply a color shift to the "edge" parts of the ninepatch.
|
// Apply a color shift to the "edge" parts of the ninepatch.
|
||||||
|
@ -46,15 +49,17 @@ public:
|
||||||
// Apply a color shift to the "center" part of the ninepatch.
|
// Apply a color shift to the "center" part of the ninepatch.
|
||||||
void setCenterColor(unsigned int centerColor);
|
void setCenterColor(unsigned int centerColor);
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
inline const Vector2f& getCornerSize() const { return mCornerSize; };
|
const Vector2f& getCornerSize() const { return mCornerSize; }
|
||||||
inline void setCornerSize(const Vector2f& size)
|
void setCornerSize(const Vector2f& size)
|
||||||
{
|
{
|
||||||
mCornerSize = size;
|
mCornerSize = size;
|
||||||
buildVertices();
|
buildVertices();
|
||||||
};
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void buildVertices();
|
void buildVertices();
|
||||||
|
|
|
@ -21,28 +21,20 @@
|
||||||
|
|
||||||
// Used to display a list of options.
|
// Used to display a list of options.
|
||||||
// Can select one or multiple options.
|
// Can select one or multiple options.
|
||||||
|
template <typename T> class OptionListComponent : public GuiComponent
|
||||||
// if !multiSelect
|
|
||||||
// * <- curEntry ->
|
|
||||||
// Always
|
|
||||||
// * press a -> open full list.
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
class OptionListComponent : public GuiComponent
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OptionListComponent(
|
OptionListComponent(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
const std::string& name,
|
||||||
const std::string& name,
|
bool multiSelect = false)
|
||||||
bool multiSelect = false)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mHelpStyle(helpstyle)
|
||||||
mHelpStyle(helpstyle),
|
, mMultiSelect(multiSelect)
|
||||||
mMultiSelect(multiSelect),
|
, mName(name)
|
||||||
mName(name),
|
, mText(window)
|
||||||
mText(window),
|
, mLeftArrow(window)
|
||||||
mLeftArrow(window),
|
, mRightArrow(window)
|
||||||
mRightArrow(window)
|
|
||||||
{
|
{
|
||||||
auto font = Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT);
|
auto font = Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT);
|
||||||
mText.setFont(font);
|
mText.setFont(font);
|
||||||
|
@ -79,15 +71,15 @@ public:
|
||||||
LOG(LogWarning) << "OptionListComponent too narrow";
|
LOG(LogWarning) << "OptionListComponent too narrow";
|
||||||
}
|
}
|
||||||
|
|
||||||
mText.setSize(mSize.x() - mLeftArrow.getSize().x() -
|
mText.setSize(mSize.x() - mLeftArrow.getSize().x() - mRightArrow.getSize().x(),
|
||||||
mRightArrow.getSize().x(), mText.getFont()->getHeight());
|
mText.getFont()->getHeight());
|
||||||
|
|
||||||
// Position.
|
// Position.
|
||||||
mLeftArrow.setPosition(0, (mSize.y() - mLeftArrow.getSize().y()) / 2);
|
mLeftArrow.setPosition(0, (mSize.y() - mLeftArrow.getSize().y()) / 2);
|
||||||
mText.setPosition(mLeftArrow.getPosition().x() + mLeftArrow.getSize().x(),
|
mText.setPosition(mLeftArrow.getPosition().x() + mLeftArrow.getSize().x(),
|
||||||
(mSize.y() - mText.getSize().y()) / 2);
|
(mSize.y() - mText.getSize().y()) / 2);
|
||||||
mRightArrow.setPosition(mText.getPosition().x() + mText.getSize().x(),
|
mRightArrow.setPosition(mText.getPosition().x() + mText.getSize().x(),
|
||||||
(mSize.y() - mRightArrow.getSize().y()) / 2);
|
(mSize.y() - mRightArrow.getSize().y()) / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override
|
bool input(InputConfig* config, Input input) override
|
||||||
|
@ -115,7 +107,6 @@ public:
|
||||||
mEntries.at(next).selected = true;
|
mEntries.at(next).selected = true;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (config->isMappedLike("right", input)) {
|
else if (config->isMappedLike("right", input)) {
|
||||||
// Ignore input if the component has been disabled.
|
// Ignore input if the component has been disabled.
|
||||||
|
@ -205,7 +196,7 @@ public:
|
||||||
void sortEntriesByName()
|
void sortEntriesByName()
|
||||||
{
|
{
|
||||||
std::sort(std::begin(mEntries), std::end(mEntries),
|
std::sort(std::begin(mEntries), std::end(mEntries),
|
||||||
[](OptionListData a, OptionListData b) { return a.name < b.name; });
|
[](OptionListData a, OptionListData b) { return a.name < b.name; });
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int getSelectedId()
|
unsigned int getSelectedId()
|
||||||
|
@ -217,11 +208,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG(LogWarning) << "OptionListComponent::getSelectedId() - "
|
LOG(LogWarning) << "OptionListComponent::getSelectedId() - "
|
||||||
"no selected element found, defaulting to 0";
|
"no selected element found, defaulting to 0";
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct OptionListData {
|
struct OptionListData {
|
||||||
|
@ -232,10 +223,7 @@ private:
|
||||||
|
|
||||||
HelpStyle mHelpStyle;
|
HelpStyle mHelpStyle;
|
||||||
|
|
||||||
void open()
|
void open() { mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName)); }
|
||||||
{
|
|
||||||
mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName));
|
|
||||||
}
|
|
||||||
|
|
||||||
void onSelectedChanged()
|
void onSelectedChanged()
|
||||||
{
|
{
|
||||||
|
@ -246,7 +234,8 @@ private:
|
||||||
mText.setText(ss.str());
|
mText.setText(ss.str());
|
||||||
mText.setSize(0, mText.getSize().y());
|
mText.setSize(0, mText.getSize().y());
|
||||||
setSize(mText.getSize().x() + mRightArrow.getSize().x() +
|
setSize(mText.getSize().x() + mRightArrow.getSize().x() +
|
||||||
24 * Renderer::getScreenWidthModifier(), mText.getSize().y());
|
24 * Renderer::getScreenWidthModifier(),
|
||||||
|
mText.getSize().y());
|
||||||
if (mParent) // Hack since there's no "on child size changed" callback.
|
if (mParent) // Hack since there's no "on child size changed" callback.
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
}
|
}
|
||||||
|
@ -257,8 +246,9 @@ private:
|
||||||
mText.setText(Utils::String::toUpper(it->name));
|
mText.setText(Utils::String::toUpper(it->name));
|
||||||
mText.setSize(0, mText.getSize().y());
|
mText.setSize(0, mText.getSize().y());
|
||||||
setSize(mText.getSize().x() + mLeftArrow.getSize().x() +
|
setSize(mText.getSize().x() + mLeftArrow.getSize().x() +
|
||||||
mRightArrow.getSize().x() +
|
mRightArrow.getSize().x() +
|
||||||
24 * Renderer::getScreenWidthModifier(), mText.getSize().y());
|
24.0f * Renderer::getScreenWidthModifier(),
|
||||||
|
mText.getSize().y());
|
||||||
if (mParent) // Hack since there's no "on child size changed" callback.
|
if (mParent) // Hack since there's no "on child size changed" callback.
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
break;
|
break;
|
||||||
|
@ -290,15 +280,14 @@ private:
|
||||||
class OptionListPopup : public GuiComponent
|
class OptionListPopup : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
OptionListPopup(
|
OptionListPopup(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
OptionListComponent<T>* parent,
|
||||||
OptionListComponent<T>* parent,
|
const std::string& title)
|
||||||
const std::string& title)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mHelpStyle(helpstyle)
|
||||||
mHelpStyle(helpstyle),
|
, mMenu(window, title.c_str())
|
||||||
mMenu(window, title.c_str()),
|
, mParent(parent)
|
||||||
mParent(parent)
|
|
||||||
{
|
{
|
||||||
auto font = Font::get(FONT_SIZE_MEDIUM);
|
auto font = Font::get(FONT_SIZE_MEDIUM);
|
||||||
ComponentListRow row;
|
ComponentListRow row;
|
||||||
|
@ -308,8 +297,9 @@ private:
|
||||||
|
|
||||||
for (auto it = mParent->mEntries.begin(); it != mParent->mEntries.end(); it++) {
|
for (auto it = mParent->mEntries.begin(); it != mParent->mEntries.end(); it++) {
|
||||||
row.elements.clear();
|
row.elements.clear();
|
||||||
row.addElement(std::make_shared<TextComponent>
|
row.addElement(std::make_shared<TextComponent>(
|
||||||
(mWindow, Utils::String::toUpper(it->name), font, 0x777777FF), true);
|
mWindow, Utils::String::toUpper(it->name), font, 0x777777FF),
|
||||||
|
true);
|
||||||
|
|
||||||
OptionListData& e = *it;
|
OptionListData& e = *it;
|
||||||
|
|
||||||
|
@ -367,7 +357,7 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f,
|
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2.0f,
|
||||||
Renderer::getScreenHeight() * 0.13f);
|
Renderer::getScreenHeight() * 0.13f);
|
||||||
addChild(&mMenu);
|
addChild(&mMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -389,7 +379,7 @@ private:
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuComponent mMenu;
|
MenuComponent mMenu;
|
||||||
|
|
|
@ -9,25 +9,23 @@
|
||||||
|
|
||||||
#include "components/RatingComponent.h"
|
#include "components/RatingComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
|
||||||
RatingComponent::RatingComponent(
|
RatingComponent::RatingComponent(Window* window, bool colorizeChanges)
|
||||||
Window* window,
|
: GuiComponent(window)
|
||||||
bool colorizeChanges)
|
, mColorShift(DEFAULT_COLORSHIFT)
|
||||||
: GuiComponent(window),
|
, mColorShiftEnd(DEFAULT_COLORSHIFT)
|
||||||
mColorShift(DEFAULT_COLORSHIFT),
|
, mUnfilledColor(DEFAULT_COLORSHIFT)
|
||||||
mColorShiftEnd(DEFAULT_COLORSHIFT),
|
, mColorizeChanges(colorizeChanges)
|
||||||
mUnfilledColor(DEFAULT_COLORSHIFT),
|
, mColorOriginalValue(DEFAULT_COLORSHIFT)
|
||||||
mColorizeChanges(colorizeChanges),
|
, mColorChangedValue(DEFAULT_COLORSHIFT)
|
||||||
mColorOriginalValue(DEFAULT_COLORSHIFT),
|
|
||||||
mColorChangedValue(DEFAULT_COLORSHIFT)
|
|
||||||
{
|
{
|
||||||
mFilledTexture = TextureResource::get(":/graphics/star_filled.svg", true);
|
mFilledTexture = TextureResource::get(":/graphics/star_filled.svg", true);
|
||||||
mUnfilledTexture = TextureResource::get(":/graphics/star_unfilled.svg", true);
|
mUnfilledTexture = TextureResource::get(":/graphics/star_unfilled.svg", true);
|
||||||
mValue = 0.5f;
|
mValue = 0.5f;
|
||||||
mSize = Vector2f(64 * NUM_RATING_STARS, 64);
|
mSize = Vector2f(64.0f * NUM_RATING_STARS, 64.0f);
|
||||||
updateVertices();
|
updateVertices();
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
@ -39,13 +37,13 @@ void RatingComponent::setValue(const std::string& value)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Round up to the closest .1 value, i.e. to the closest half-icon.
|
// Round up to the closest .1 value, i.e. to the closest half-icon.
|
||||||
mValue = ceilf(stof(value) / 0.1f) / 10;
|
mValue = ceilf(stof(value) / 0.1f) / 10.0f;
|
||||||
mOriginalValue = static_cast<int>(mValue * 10);
|
mOriginalValue = static_cast<int>(mValue * 10.0f);
|
||||||
|
|
||||||
// If the argument to colorize the rating icons has been passed, set the
|
// If the argument to colorize the rating icons has been passed, set the
|
||||||
// color shift accordingly.
|
// color shift accordingly.
|
||||||
if (mColorizeChanges) {
|
if (mColorizeChanges) {
|
||||||
if (static_cast<int>(mValue * 10) == mOriginalValue)
|
if (static_cast<int>(mValue * 10.0f) == mOriginalValue)
|
||||||
setColorShift(mColorOriginalValue);
|
setColorShift(mColorOriginalValue);
|
||||||
else
|
else
|
||||||
setColorShift(mColorChangedValue);
|
setColorShift(mColorChangedValue);
|
||||||
|
@ -122,6 +120,7 @@ void RatingComponent::updateVertices()
|
||||||
const float fw = getSize().y() * numStars;
|
const float fw = getSize().y() * numStars;
|
||||||
const unsigned int color = Renderer::convertRGBAToABGR(mColorShift);
|
const unsigned int color = Renderer::convertRGBAToABGR(mColorShift);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
mVertices[0] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
|
mVertices[0] = { { 0.0f, 0.0f }, { 0.0f, 1.0f }, color };
|
||||||
mVertices[1] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
mVertices[1] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
||||||
mVertices[2] = { { w, 0.0f }, { mValue * numStars, 1.0f }, color };
|
mVertices[2] = { { w, 0.0f }, { mValue * numStars, 1.0f }, color };
|
||||||
|
@ -131,13 +130,7 @@ void RatingComponent::updateVertices()
|
||||||
mVertices[5] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
mVertices[5] = { { 0.0f, h }, { 0.0f, 0.0f }, color };
|
||||||
mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color };
|
mVertices[6] = { { fw, 0.0f }, { numStars, 1.0f }, color };
|
||||||
mVertices[7] = { { fw, h }, { numStars, 0.0f }, color };
|
mVertices[7] = { { fw, h }, { numStars, 0.0f }, color };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Disabled this code as it caused subtle but strange rendering errors
|
|
||||||
// where the icons changed size slightly when changing rating scores.
|
|
||||||
// // Round vertices.
|
|
||||||
// for (int i = 0; i < 8; i++)
|
|
||||||
// mVertices[i].pos.round();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RatingComponent::updateColors()
|
void RatingComponent::updateColors()
|
||||||
|
@ -159,8 +152,8 @@ void RatingComponent::render(const Transform4x4f& parentTrans)
|
||||||
|
|
||||||
if (mOpacity > 0) {
|
if (mOpacity > 0) {
|
||||||
if (Settings::getInstance()->getBool("DebugImage")) {
|
if (Settings::getInstance()->getBool("DebugImage")) {
|
||||||
Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS,
|
Renderer::drawRect(0.0f, 0.0f, mSize.y() * NUM_RATING_STARS, mSize.y(), 0xFF000033,
|
||||||
mSize.y(), 0xFF000033, 0xFF000033);
|
0xFF000033);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mUnfilledTexture->bind()) {
|
if (mUnfilledTexture->bind()) {
|
||||||
|
@ -189,14 +182,14 @@ void RatingComponent::render(const Transform4x4f& parentTrans)
|
||||||
bool RatingComponent::input(InputConfig* config, Input input)
|
bool RatingComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if (config->isMappedTo("a", input) && input.value != 0) {
|
if (config->isMappedTo("a", input) && input.value != 0) {
|
||||||
mValue += (1.f/2) / NUM_RATING_STARS;
|
mValue += (1.0f / 2.0f) / NUM_RATING_STARS;
|
||||||
if (mValue > 1.05f)
|
if (mValue > 1.05f)
|
||||||
mValue = 0.0f;
|
mValue = 0.0f;
|
||||||
|
|
||||||
// If the argument to colorize the rating icons has been passed,
|
// If the argument to colorize the rating icons has been passed,
|
||||||
// set the color shift accordingly.
|
// set the color shift accordingly.
|
||||||
if (mColorizeChanges) {
|
if (mColorizeChanges) {
|
||||||
if (static_cast<int>(mValue * 10) == mOriginalValue)
|
if (static_cast<int>(mValue * 10.0f) == mOriginalValue)
|
||||||
setColorShift(mColorOriginalValue);
|
setColorShift(mColorOriginalValue);
|
||||||
else
|
else
|
||||||
setColorShift(mColorChangedValue);
|
setColorShift(mColorChangedValue);
|
||||||
|
@ -208,7 +201,9 @@ bool RatingComponent::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
|
|
||||||
void RatingComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void RatingComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
|
|
|
@ -10,18 +10,13 @@
|
||||||
#ifndef ES_APP_COMPONENTS_RATING_COMPONENT_H
|
#ifndef ES_APP_COMPONENTS_RATING_COMPONENT_H
|
||||||
#define ES_APP_COMPONENTS_RATING_COMPONENT_H
|
#define ES_APP_COMPONENTS_RATING_COMPONENT_H
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
class TextureResource;
|
class TextureResource;
|
||||||
|
|
||||||
#define NUM_RATING_STARS 5
|
#define NUM_RATING_STARS 5
|
||||||
|
|
||||||
// Used to visually display/edit some sort of "score" - e.g. 5/10, 3/5, etc.
|
|
||||||
// setSize(x, y) works a little differently than you might expect:
|
|
||||||
// * (0, y != 0) - x will be automatically calculated (5*y).
|
|
||||||
// * (x != 0, 0) - y will be automatically calculated (x/5).
|
|
||||||
// * (x != 0, y != 0) - you better be sure x = y*5.
|
|
||||||
class RatingComponent : public GuiComponent
|
class RatingComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -40,13 +35,15 @@ public:
|
||||||
|
|
||||||
// Multiply all pixels in the image by this color when rendering.
|
// Multiply all pixels in the image by this color when rendering.
|
||||||
void setColorShift(unsigned int color) override;
|
void setColorShift(unsigned int color) override;
|
||||||
unsigned int getColorShift() const override { return mColorShift; };
|
unsigned int getColorShift() const override { return mColorShift; }
|
||||||
|
|
||||||
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; };
|
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }
|
||||||
void setChangedColor(unsigned int color) override { mColorChangedValue = color; };
|
void setChangedColor(unsigned int color) override { mColorChangedValue = color; }
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
|
|
|
@ -9,22 +9,21 @@
|
||||||
|
|
||||||
#include "components/ScrollableContainer.h"
|
#include "components/ScrollableContainer.h"
|
||||||
|
|
||||||
|
#include "Window.h"
|
||||||
#include "animations/LambdaAnimation.h"
|
#include "animations/LambdaAnimation.h"
|
||||||
#include "math/Vector2i.h"
|
#include "math/Vector2i.h"
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
ScrollableContainer::ScrollableContainer(
|
ScrollableContainer::ScrollableContainer(Window* window)
|
||||||
Window* window)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mAutoScrollDelay(0)
|
||||||
mAutoScrollDelay(0),
|
, mAutoScrollSpeed(0)
|
||||||
mAutoScrollSpeed(0),
|
, mAutoScrollAccumulator(0)
|
||||||
mAutoScrollAccumulator(0),
|
, mScrollPos(0, 0)
|
||||||
mScrollPos(0, 0),
|
, mScrollDir(0, 0)
|
||||||
mScrollDir(0, 0),
|
, mAutoScrollResetAccumulator(0)
|
||||||
mAutoScrollResetAccumulator(0),
|
, mFontSize(0.0f)
|
||||||
mFontSize(0.0f)
|
|
||||||
{
|
{
|
||||||
// Set the modifier to get equivalent scrolling speed regardless of screen resolution.
|
// Set the modifier to get equivalent scrolling speed regardless of screen resolution.
|
||||||
mResolutionModifier = Renderer::getScreenHeightModifier();
|
mResolutionModifier = Renderer::getScreenHeightModifier();
|
||||||
|
@ -33,7 +32,8 @@ ScrollableContainer::ScrollableContainer(
|
||||||
// For narrower aspect ratios than 16:9 there is a need to set an additional compensation
|
// For narrower aspect ratios than 16:9 there is a need to set an additional compensation
|
||||||
// to get somehow consistent scrolling speeds.
|
// to get somehow consistent scrolling speeds.
|
||||||
float aspectCompensation = static_cast<float>(Renderer::getScreenHeight()) /
|
float aspectCompensation = static_cast<float>(Renderer::getScreenHeight()) /
|
||||||
static_cast<float>(Renderer::getScreenWidth()) - 0.5625f;
|
static_cast<float>(Renderer::getScreenWidth()) -
|
||||||
|
0.5625f;
|
||||||
if (aspectCompensation > 0)
|
if (aspectCompensation > 0)
|
||||||
mResolutionModifier += aspectCompensation * 4.0f;
|
mResolutionModifier += aspectCompensation * 4.0f;
|
||||||
|
|
||||||
|
@ -42,24 +42,14 @@ ScrollableContainer::ScrollableContainer(
|
||||||
mAutoScrollSpeedConstant = AUTO_SCROLL_SPEED;
|
mAutoScrollSpeedConstant = AUTO_SCROLL_SPEED;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f ScrollableContainer::getScrollPos() const
|
|
||||||
{
|
|
||||||
return mScrollPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScrollableContainer::setScrollPos(const Vector2f& pos)
|
|
||||||
{
|
|
||||||
mScrollPos = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ScrollableContainer::setAutoScroll(bool autoScroll)
|
void ScrollableContainer::setAutoScroll(bool autoScroll)
|
||||||
{
|
{
|
||||||
if (autoScroll) {
|
if (autoScroll) {
|
||||||
mScrollDir = Vector2f(0, 1);
|
mScrollDir = Vector2f(0, 1);
|
||||||
mAutoScrollDelay = static_cast<int>(mAutoScrollDelayConstant);
|
mAutoScrollDelay = static_cast<int>(mAutoScrollDelayConstant);
|
||||||
mAutoScrollSpeed = mAutoScrollSpeedConstant;
|
mAutoScrollSpeed = mAutoScrollSpeedConstant;
|
||||||
mAutoScrollSpeed = static_cast<int>(static_cast<float>(mAutoScrollSpeedConstant) /
|
mAutoScrollSpeed =
|
||||||
mResolutionModifier);
|
static_cast<int>(static_cast<float>(mAutoScrollSpeedConstant) / mResolutionModifier);
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -71,7 +61,8 @@ void ScrollableContainer::setAutoScroll(bool autoScroll)
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::setScrollParameters(float autoScrollDelayConstant,
|
void ScrollableContainer::setScrollParameters(float autoScrollDelayConstant,
|
||||||
float autoScrollResetDelayConstant, int autoScrollSpeedConstant)
|
float autoScrollResetDelayConstant,
|
||||||
|
int autoScrollSpeedConstant)
|
||||||
{
|
{
|
||||||
mAutoScrollResetDelayConstant = autoScrollResetDelayConstant;
|
mAutoScrollResetDelayConstant = autoScrollResetDelayConstant;
|
||||||
mAutoScrollDelayConstant = autoScrollDelayConstant;
|
mAutoScrollDelayConstant = autoScrollDelayConstant;
|
||||||
|
@ -90,7 +81,7 @@ void ScrollableContainer::update(int deltaTime)
|
||||||
{
|
{
|
||||||
// Don't scroll if the media viewer or screensaver is active or if text scrolling is disabled;
|
// Don't scroll if the media viewer or screensaver is active or if text scrolling is disabled;
|
||||||
if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() ||
|
if (mWindow->isMediaViewerActive() || mWindow->isScreensaverActive() ||
|
||||||
!mWindow->getAllowTextScrolling()) {
|
!mWindow->getAllowTextScrolling()) {
|
||||||
if (mScrollPos != 0 && !mWindow->isLaunchScreenDisplayed())
|
if (mScrollPos != 0 && !mWindow->isLaunchScreenDisplayed())
|
||||||
reset();
|
reset();
|
||||||
return;
|
return;
|
||||||
|
@ -105,8 +96,8 @@ void ScrollableContainer::update(int deltaTime)
|
||||||
|
|
||||||
// Also adjust the scrolling speed based on the size of the font.
|
// Also adjust the scrolling speed based on the size of the font.
|
||||||
float fontSizeModifier = mSmallFontSize / mFontSize;
|
float fontSizeModifier = mSmallFontSize / mFontSize;
|
||||||
adjustedAutoScrollSpeed = static_cast<int>(adjustedAutoScrollSpeed *
|
adjustedAutoScrollSpeed =
|
||||||
fontSizeModifier * fontSizeModifier);
|
static_cast<int>(adjustedAutoScrollSpeed * fontSizeModifier * fontSizeModifier);
|
||||||
|
|
||||||
if (adjustedAutoScrollSpeed < 0)
|
if (adjustedAutoScrollSpeed < 0)
|
||||||
adjustedAutoScrollSpeed = 1;
|
adjustedAutoScrollSpeed = 1;
|
||||||
|
@ -165,11 +156,11 @@ void ScrollableContainer::render(const Transform4x4f& parentTrans)
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
Vector2i clipPos(static_cast<int>(trans.translation().x()),
|
Vector2i clipPos(static_cast<int>(trans.translation().x()),
|
||||||
static_cast<int>(trans.translation().y()));
|
static_cast<int>(trans.translation().y()));
|
||||||
|
|
||||||
Vector3f dimScaled = trans * Vector3f(mSize.x(), mSize.y(), 0);
|
Vector3f dimScaled = trans * Vector3f(mSize.x(), mSize.y(), 0);
|
||||||
Vector2i clipDim(static_cast<int>((dimScaled.x()) - trans.translation().x()),
|
Vector2i clipDim(static_cast<int>((dimScaled.x()) - trans.translation().x()),
|
||||||
static_cast<int>((dimScaled.y()) - trans.translation().y()));
|
static_cast<int>((dimScaled.y()) - trans.translation().y()));
|
||||||
|
|
||||||
Renderer::pushClipRect(clipPos, clipDim);
|
Renderer::pushClipRect(clipPos, clipDim);
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,13 @@ class ScrollableContainer : public GuiComponent
|
||||||
public:
|
public:
|
||||||
ScrollableContainer(Window* window);
|
ScrollableContainer(Window* window);
|
||||||
|
|
||||||
Vector2f getScrollPos() const;
|
Vector2f getScrollPos() const { return mScrollPos; }
|
||||||
void setScrollPos(const Vector2f& pos);
|
void setScrollPos(const Vector2f& pos) { mScrollPos = pos; }
|
||||||
|
|
||||||
void setAutoScroll(bool autoScroll);
|
void setAutoScroll(bool autoScroll);
|
||||||
void setScrollParameters(float autoScrollDelayConstant, float autoScrollResetDelayConstant,
|
void setScrollParameters(float autoScrollDelayConstant,
|
||||||
int autoScrollSpeedConstant) override;
|
float autoScrollResetDelayConstant,
|
||||||
|
int autoScrollSpeedConstant) override;
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
|
|
|
@ -14,23 +14,19 @@
|
||||||
#define MOVE_REPEAT_RATE 40
|
#define MOVE_REPEAT_RATE 40
|
||||||
|
|
||||||
SliderComponent::SliderComponent(
|
SliderComponent::SliderComponent(
|
||||||
Window* window,
|
Window* window, float min, float max, float increment, const std::string& suffix)
|
||||||
float min,
|
: GuiComponent(window)
|
||||||
float max,
|
, mMin(min)
|
||||||
float increment,
|
, mMax(max)
|
||||||
const std::string& suffix)
|
, mSingleIncrement(increment)
|
||||||
: GuiComponent(window),
|
, mMoveRate(0)
|
||||||
mMin(min),
|
, mKnob(window)
|
||||||
mMax(max),
|
, mSuffix(suffix)
|
||||||
mSingleIncrement(increment),
|
|
||||||
mMoveRate(0),
|
|
||||||
mKnob(window),
|
|
||||||
mSuffix(suffix)
|
|
||||||
{
|
{
|
||||||
assert((min - max) != 0);
|
assert((min - max) != 0);
|
||||||
|
|
||||||
// Some sane default value.
|
// Some sane default value.
|
||||||
mValue = (max + min) / 2;
|
mValue = (max + min) / 2.0f;
|
||||||
|
|
||||||
mKnob.setOrigin(0.5f, 0.5f);
|
mKnob.setOrigin(0.5f, 0.5f);
|
||||||
mKnob.setImage(":/graphics/slider_knob.svg");
|
mKnob.setImage(":/graphics/slider_knob.svg");
|
||||||
|
@ -87,14 +83,15 @@ void SliderComponent::render(const Transform4x4f& parentTrans)
|
||||||
if (mValueCache)
|
if (mValueCache)
|
||||||
mFont->renderTextCache(mValueCache.get());
|
mFont->renderTextCache(mValueCache.get());
|
||||||
|
|
||||||
float width = mSize.x() - mKnob.getSize().x() -
|
float width =
|
||||||
(mValueCache ? mValueCache->metrics.size.x() +
|
mSize.x() - mKnob.getSize().x() -
|
||||||
(4 * Renderer::getScreenWidthModifier()) : 0);
|
(mValueCache ? mValueCache->metrics.size.x() + (4.0f * Renderer::getScreenWidthModifier()) :
|
||||||
|
0);
|
||||||
|
|
||||||
// Render line.
|
// Render line.
|
||||||
const float lineWidth = 2 * Renderer::getScreenHeightModifier();;
|
const float lineWidth = 2.0f * Renderer::getScreenHeightModifier();
|
||||||
Renderer::drawRect(mKnob.getSize().x() / 2, mSize.y() / 2 -
|
Renderer::drawRect(mKnob.getSize().x() / 2.0f, mSize.y() / 2.0f - lineWidth / 2.0f, width,
|
||||||
lineWidth / 2, width, lineWidth, 0x777777FF, 0x777777FF);
|
lineWidth, 0x777777FF, 0x777777FF);
|
||||||
|
|
||||||
// Render knob.
|
// Render knob.
|
||||||
mKnob.render(trans);
|
mKnob.render(trans);
|
||||||
|
@ -113,10 +110,7 @@ void SliderComponent::setValue(float value)
|
||||||
onValueChanged();
|
onValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
float SliderComponent::getValue()
|
float SliderComponent::getValue() { return mValue; }
|
||||||
{
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SliderComponent::onSizeChanged()
|
void SliderComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
|
@ -146,18 +140,20 @@ void SliderComponent::onValueChanged()
|
||||||
const std::string max = ss.str();
|
const std::string max = ss.str();
|
||||||
|
|
||||||
Vector2f textSize = mFont->sizeText(max);
|
Vector2f textSize = mFont->sizeText(max);
|
||||||
mValueCache = std::shared_ptr<TextCache>(mFont->buildTextCache(val, mSize.x() -
|
mValueCache = std::shared_ptr<TextCache>(mFont->buildTextCache(
|
||||||
textSize.x(), (mSize.y() - textSize.y()) / 2, 0x777777FF));
|
val, mSize.x() - textSize.x(), (mSize.y() - textSize.y()) / 2.0f, 0x777777FF));
|
||||||
mValueCache->metrics.size[0] = textSize.x(); // Fudge the width.
|
mValueCache->metrics.size[0] = textSize.x(); // Fudge the width.
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update knob position/size.
|
// Update knob position/size.
|
||||||
mKnob.setResize(0, mSize.y() * 0.7f);
|
mKnob.setResize(0, mSize.y() * 0.7f);
|
||||||
float lineLength = mSize.x() - mKnob.getSize().x() -
|
float lineLength =
|
||||||
(mValueCache ? mValueCache->metrics.size.x() +
|
mSize.x() - mKnob.getSize().x() -
|
||||||
(4 * Renderer::getScreenWidthModifier()) : 0);
|
(mValueCache ? mValueCache->metrics.size.x() + (4.0f * Renderer::getScreenWidthModifier()) :
|
||||||
mKnob.setPosition(((mValue - mMin / 2) / mMax) * lineLength +
|
0);
|
||||||
mKnob.getSize().x() / 2, mSize.y() / 2);
|
|
||||||
|
mKnob.setPosition(((mValue - mMin / 2.0f) / mMax) * lineLength + mKnob.getSize().x() / 2.0f,
|
||||||
|
mSize.y() / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
|
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
#define ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "components/ImageComponent.h"
|
||||||
|
|
||||||
class Font;
|
class Font;
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
@ -20,13 +20,9 @@ class SliderComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Minimum value (far left of the slider), maximum value (far right of the slider),
|
// Minimum value (far left of the slider), maximum value (far right of the slider),
|
||||||
// increment size (how much just pressing L/R moves by), unit to display (optional).
|
// increment size (how much pressing L/R moves by), unit to display (optional).
|
||||||
SliderComponent(
|
SliderComponent(
|
||||||
Window* window,
|
Window* window, float min, float max, float increment, const std::string& suffix = "");
|
||||||
float min,
|
|
||||||
float max,
|
|
||||||
float increment,
|
|
||||||
const std::string& suffix = "");
|
|
||||||
|
|
||||||
void setValue(float val);
|
void setValue(float val);
|
||||||
float getValue();
|
float getValue();
|
||||||
|
|
|
@ -10,31 +10,19 @@
|
||||||
|
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
|
|
||||||
SwitchComponent::SwitchComponent(
|
SwitchComponent::SwitchComponent(Window* window, bool state)
|
||||||
Window* window,
|
: GuiComponent(window)
|
||||||
bool state)
|
, mImage(window)
|
||||||
: GuiComponent(window),
|
, mState(state)
|
||||||
mImage(window),
|
, mOriginalValue(state)
|
||||||
mState(state),
|
, mColorOriginalValue(DEFAULT_COLORSHIFT)
|
||||||
mOriginalValue(state),
|
, mColorChangedValue(DEFAULT_COLORSHIFT)
|
||||||
mColorOriginalValue(DEFAULT_COLORSHIFT),
|
|
||||||
mColorChangedValue(DEFAULT_COLORSHIFT)
|
|
||||||
{
|
{
|
||||||
mImage.setImage(":/graphics/off.svg");
|
mImage.setImage(":/graphics/off.svg");
|
||||||
mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
|
mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
|
||||||
mSize = mImage.getSize();
|
mSize = mImage.getSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwitchComponent::onSizeChanged()
|
|
||||||
{
|
|
||||||
mImage.setSize(mSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::setResize(float width, float height)
|
|
||||||
{
|
|
||||||
mImage.setResize(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool SwitchComponent::input(InputConfig* config, Input input)
|
bool SwitchComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if (config->isMappedTo("a", input) && input.value) {
|
if (config->isMappedTo("a", input) && input.value) {
|
||||||
|
@ -61,21 +49,13 @@ void SwitchComponent::render(const Transform4x4f& parentTrans)
|
||||||
renderChildren(trans);
|
renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SwitchComponent::getState() const
|
|
||||||
{
|
|
||||||
return mState;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::setState(bool state)
|
void SwitchComponent::setState(bool state)
|
||||||
{
|
{
|
||||||
mState = state;
|
mState = state;
|
||||||
onStateChanged();
|
onStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SwitchComponent::getValue() const
|
std::string SwitchComponent::getValue() const { return mState ? "true" : "false"; }
|
||||||
{
|
|
||||||
return mState ? "true" : "false";
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::setValue(const std::string& statestring)
|
void SwitchComponent::setValue(const std::string& statestring)
|
||||||
{
|
{
|
||||||
|
@ -88,26 +68,6 @@ void SwitchComponent::setValue(const std::string& statestring)
|
||||||
onStateChanged();
|
onStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char SwitchComponent::getOpacity() const
|
|
||||||
{
|
|
||||||
return mImage.getOpacity();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::setOpacity(unsigned char opacity)
|
|
||||||
{
|
|
||||||
mImage.setOpacity(opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::setColorShift(unsigned int color)
|
|
||||||
{
|
|
||||||
mImage.setColorShift(color);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int SwitchComponent::getColorShift() const
|
|
||||||
{
|
|
||||||
return mImage.getColorShift();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SwitchComponent::onStateChanged()
|
void SwitchComponent::onStateChanged()
|
||||||
{
|
{
|
||||||
mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg");
|
mImage.setImage(mState ? ":/graphics/on.svg" : ":/graphics/off.svg");
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
|
#define ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "components/ImageComponent.h"
|
||||||
|
|
||||||
// A simple "on/off" switch.
|
// A simple "on/off" switch.
|
||||||
class SwitchComponent : public GuiComponent
|
class SwitchComponent : public GuiComponent
|
||||||
|
@ -20,25 +20,25 @@ public:
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override { mImage.setSize(mSize); }
|
||||||
|
|
||||||
void setResize(float width, float height) override;
|
void setResize(float width, float height) override { mImage.setResize(width, height); }
|
||||||
|
|
||||||
bool getState() const;
|
bool getState() const { return mState; }
|
||||||
void setState(bool state);
|
void setState(bool state);
|
||||||
std::string getValue() const override;
|
std::string getValue() const override;
|
||||||
void setValue(const std::string& statestring) override;
|
void setValue(const std::string& statestring) override;
|
||||||
|
|
||||||
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; };
|
void setOriginalColor(unsigned int color) override { mColorOriginalValue = color; }
|
||||||
void setChangedColor(unsigned int color) override { mColorChangedValue = color; };
|
void setChangedColor(unsigned int color) override { mColorChangedValue = color; }
|
||||||
void setCallback(const std::function<void()>& callbackFunc) { mToggleCallback = callbackFunc; };
|
void setCallback(const std::function<void()>& callbackFunc) { mToggleCallback = callbackFunc; }
|
||||||
|
|
||||||
unsigned char getOpacity() const override;
|
unsigned char getOpacity() const override { return mImage.getOpacity(); }
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override { mImage.setOpacity(opacity); }
|
||||||
// Multiply all pixels in the image by this color when rendering.
|
// Multiply all pixels in the image by this color when rendering.
|
||||||
void setColorShift(unsigned int color) override;
|
void setColorShift(unsigned int color) override { mImage.setColorShift(color); }
|
||||||
|
|
||||||
unsigned int getColorShift() const override;
|
unsigned int getColorShift() const override { return mImage.getColorShift(); }
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
|
|
|
@ -8,49 +8,47 @@
|
||||||
|
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
TextComponent::TextComponent(
|
TextComponent::TextComponent(Window* window)
|
||||||
Window* window)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mFont(Font::get(FONT_SIZE_MEDIUM))
|
||||||
mFont(Font::get(FONT_SIZE_MEDIUM)),
|
, mUppercase(false)
|
||||||
mUppercase(false),
|
, mColor(0x000000FF)
|
||||||
mColor(0x000000FF),
|
, mAutoCalcExtent(true, true)
|
||||||
mAutoCalcExtent(true, true),
|
, mHorizontalAlignment(ALIGN_LEFT)
|
||||||
mHorizontalAlignment(ALIGN_LEFT),
|
, mVerticalAlignment(ALIGN_CENTER)
|
||||||
mVerticalAlignment(ALIGN_CENTER),
|
, mLineSpacing(1.5f)
|
||||||
mLineSpacing(1.5f),
|
, mNoTopMargin(false)
|
||||||
mNoTopMargin(false),
|
, mBgColor(0)
|
||||||
mBgColor(0),
|
, mMargin(0.0f)
|
||||||
mMargin(0.0f),
|
, mRenderBackground(false)
|
||||||
mRenderBackground(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TextComponent::TextComponent(
|
TextComponent::TextComponent(Window* window,
|
||||||
Window* window,
|
const std::string& text,
|
||||||
const std::string& text,
|
const std::shared_ptr<Font>& font,
|
||||||
const std::shared_ptr<Font>& font,
|
unsigned int color,
|
||||||
unsigned int color,
|
Alignment align,
|
||||||
Alignment align,
|
Vector3f pos,
|
||||||
Vector3f pos,
|
Vector2f size,
|
||||||
Vector2f size,
|
unsigned int bgcolor,
|
||||||
unsigned int bgcolor,
|
float margin)
|
||||||
float margin)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mFont(nullptr)
|
||||||
mFont(nullptr),
|
, mUppercase(false)
|
||||||
mUppercase(false),
|
, mColor(0x000000FF)
|
||||||
mColor(0x000000FF),
|
, mAutoCalcExtent(true, true)
|
||||||
mAutoCalcExtent(true, true),
|
, mHorizontalAlignment(align)
|
||||||
mHorizontalAlignment(align),
|
, mVerticalAlignment(ALIGN_CENTER)
|
||||||
mVerticalAlignment(ALIGN_CENTER),
|
, mLineSpacing(1.5f)
|
||||||
mLineSpacing(1.5f),
|
, mNoTopMargin(false)
|
||||||
mNoTopMargin(false),
|
, mBgColor(0)
|
||||||
mBgColor(0),
|
, mMargin(margin)
|
||||||
mMargin(margin),
|
, mRenderBackground(false)
|
||||||
mRenderBackground(false)
|
|
||||||
{
|
{
|
||||||
setFont(font);
|
setFont(font);
|
||||||
setColor(color);
|
setColor(color);
|
||||||
|
@ -87,45 +85,30 @@ void TextComponent::setBackgroundColor(unsigned int color)
|
||||||
mBgColorOpacity = mBgColor & 0x000000FF;
|
mBgColorOpacity = mBgColor & 0x000000FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setRenderBackground(bool render)
|
|
||||||
{
|
|
||||||
mRenderBackground = render;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale the opacity.
|
// Scale the opacity.
|
||||||
void TextComponent::setOpacity(unsigned char opacity)
|
void TextComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
// This function is mostly called to do fading in-out of the Text component element.
|
// This function is mostly called to do fade in and fade out of the text component element.
|
||||||
// Therefore, we assume here that opacity is a fractional value (expressed as an int 0-255),
|
// Therefore we assume here that opacity is a fractional value (expressed as an unsigned
|
||||||
// of the opacity originally set with setColor() or setBackgroundColor().
|
// char 0 - 255) of the opacity originally set with setColor() or setBackgroundColor().
|
||||||
unsigned char o = static_cast<unsigned char>(static_cast<float>(opacity) / 255.f *
|
unsigned char o = static_cast<unsigned char>(static_cast<float>(opacity) / 255.0f *
|
||||||
static_cast<float>(mColorOpacity));
|
static_cast<float>(mColorOpacity));
|
||||||
mColor = (mColor & 0xFFFFFF00) | static_cast<unsigned char>(o);
|
mColor = (mColor & 0xFFFFFF00) | static_cast<unsigned char>(o);
|
||||||
|
|
||||||
unsigned char bgo = static_cast<unsigned char>(static_cast<float>(opacity) / 255.f *
|
unsigned char bgo = static_cast<unsigned char>(static_cast<float>(opacity) / 255.0f *
|
||||||
static_cast<float>(mBgColorOpacity));
|
static_cast<float>(mBgColorOpacity));
|
||||||
mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(bgo);
|
mBgColor = (mBgColor & 0xFFFFFF00) | static_cast<unsigned char>(bgo);
|
||||||
|
|
||||||
onColorChanged();
|
onColorChanged();
|
||||||
GuiComponent::setOpacity(opacity);
|
GuiComponent::setOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char TextComponent::getOpacity() const
|
|
||||||
{
|
|
||||||
return mColor & 0x000000FF;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextComponent::setText(const std::string& text)
|
void TextComponent::setText(const std::string& text)
|
||||||
{
|
{
|
||||||
mText = text;
|
mText = text;
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setHiddenText(const std::string& text)
|
|
||||||
{
|
|
||||||
mHiddenText = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextComponent::setUppercase(bool uppercase)
|
void TextComponent::setUppercase(bool uppercase)
|
||||||
{
|
{
|
||||||
mUppercase = uppercase;
|
mUppercase = uppercase;
|
||||||
|
@ -148,17 +131,21 @@ void TextComponent::render(const Transform4x4f& parentTrans)
|
||||||
const Vector2f& textSize = mTextCache->metrics.size;
|
const Vector2f& textSize = mTextCache->metrics.size;
|
||||||
float yOff = 0;
|
float yOff = 0;
|
||||||
switch (mVerticalAlignment) {
|
switch (mVerticalAlignment) {
|
||||||
case ALIGN_TOP:
|
case ALIGN_TOP: {
|
||||||
yOff = 0;
|
yOff = 0;
|
||||||
break;
|
break;
|
||||||
case ALIGN_BOTTOM:
|
}
|
||||||
|
case ALIGN_BOTTOM: {
|
||||||
yOff = (getSize().y() - textSize.y());
|
yOff = (getSize().y() - textSize.y());
|
||||||
break;
|
break;
|
||||||
case ALIGN_CENTER:
|
}
|
||||||
|
case ALIGN_CENTER: {
|
||||||
yOff = (getSize().y() - textSize.y()) / 2.0f;
|
yOff = (getSize().y() - textSize.y()) / 2.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
|
default: {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Vector3f off(0, yOff, 0);
|
Vector3f off(0, yOff, 0);
|
||||||
|
|
||||||
|
@ -174,22 +161,26 @@ void TextComponent::render(const Transform4x4f& parentTrans)
|
||||||
// Draw the text area, where the text actually is located.
|
// Draw the text area, where the text actually is located.
|
||||||
if (Settings::getInstance()->getBool("DebugText")) {
|
if (Settings::getInstance()->getBool("DebugText")) {
|
||||||
switch (mHorizontalAlignment) {
|
switch (mHorizontalAlignment) {
|
||||||
case ALIGN_LEFT:
|
case ALIGN_LEFT: {
|
||||||
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
||||||
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
||||||
break;
|
break;
|
||||||
case ALIGN_CENTER:
|
}
|
||||||
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f,
|
case ALIGN_CENTER: {
|
||||||
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f,
|
||||||
0x00000033, 0x00000033);
|
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
||||||
break;
|
0x00000033, 0x00000033);
|
||||||
case ALIGN_RIGHT:
|
break;
|
||||||
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f,
|
}
|
||||||
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
case ALIGN_RIGHT: {
|
||||||
0x00000033, 0x00000033);
|
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f,
|
||||||
break;
|
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
||||||
default:
|
0x00000033, 0x00000033);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mFont->renderTextCache(mTextCache.get());
|
mFont->renderTextCache(mTextCache.get());
|
||||||
|
@ -203,8 +194,10 @@ void TextComponent::calculateExtent()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (mAutoCalcExtent.y())
|
if (mAutoCalcExtent.y())
|
||||||
mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText)
|
mSize[1] = mFont
|
||||||
: mText, getSize().x(), mLineSpacing).y();
|
->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText,
|
||||||
|
getSize().x(), mLineSpacing)
|
||||||
|
.y();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,14 +239,14 @@ void TextComponent::onTextChanged()
|
||||||
|
|
||||||
text.append(abbrev);
|
text.append(abbrev);
|
||||||
|
|
||||||
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, Vector2f(0, 0),
|
mTextCache = std::shared_ptr<TextCache>(
|
||||||
(mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment,
|
f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
||||||
mLineSpacing, mNoTopMargin));
|
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(f->wrapText(
|
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(
|
||||||
text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
f->wrapText(text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
||||||
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
|
mHorizontalAlignment, mLineSpacing, mNoTopMargin));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -269,10 +262,7 @@ void TextComponent::setHorizontalAlignment(Alignment align)
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setVerticalAlignment(Alignment align)
|
void TextComponent::setVerticalAlignment(Alignment align) { mVerticalAlignment = align; }
|
||||||
{
|
|
||||||
mVerticalAlignment = align;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextComponent::setLineSpacing(float spacing)
|
void TextComponent::setLineSpacing(float spacing)
|
||||||
{
|
{
|
||||||
|
@ -286,28 +276,10 @@ void TextComponent::setNoTopMargin(bool margin)
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextComponent::getValue() const
|
void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
{
|
const std::string& view,
|
||||||
return mText;
|
const std::string& element,
|
||||||
}
|
unsigned int properties)
|
||||||
|
|
||||||
void TextComponent::setValue(const std::string& value)
|
|
||||||
{
|
|
||||||
setText(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string TextComponent::getHiddenValue() const
|
|
||||||
{
|
|
||||||
return mHiddenText;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextComponent::setHiddenValue(const std::string& value)
|
|
||||||
{
|
|
||||||
setHiddenText(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
|
||||||
const std::string& element, unsigned int properties)
|
|
||||||
{
|
{
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
#define ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
||||||
|
|
||||||
#include "resources/Font.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "resources/Font.h"
|
||||||
|
|
||||||
class ThemeData;
|
class ThemeData;
|
||||||
|
|
||||||
|
@ -25,46 +25,47 @@ class TextComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextComponent(Window* window);
|
TextComponent(Window* window);
|
||||||
TextComponent(
|
TextComponent(Window* window,
|
||||||
Window* window,
|
const std::string& text,
|
||||||
const std::string& text,
|
const std::shared_ptr<Font>& font,
|
||||||
const std::shared_ptr<Font>& font,
|
unsigned int color = 0x000000FF,
|
||||||
unsigned int color = 0x000000FF,
|
Alignment align = ALIGN_LEFT,
|
||||||
Alignment align = ALIGN_LEFT,
|
Vector3f pos = Vector3f::Zero(),
|
||||||
Vector3f pos = Vector3f::Zero(),
|
Vector2f size = Vector2f::Zero(),
|
||||||
Vector2f size = Vector2f::Zero(),
|
unsigned int bgcolor = 0x00000000,
|
||||||
unsigned int bgcolor = 0x00000000,
|
float margin = 0.0f);
|
||||||
float margin = 0.0f);
|
|
||||||
|
|
||||||
void setFont(const std::shared_ptr<Font>& font);
|
void setFont(const std::shared_ptr<Font>& font);
|
||||||
void setUppercase(bool uppercase);
|
void setUppercase(bool uppercase);
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void setText(const std::string& text);
|
void setText(const std::string& text);
|
||||||
void setHiddenText(const std::string& text);
|
void setHiddenText(const std::string& text) { mHiddenText = text; }
|
||||||
void setColor(unsigned int color) override;
|
void setColor(unsigned int color) override;
|
||||||
void setHorizontalAlignment(Alignment align);
|
void setHorizontalAlignment(Alignment align);
|
||||||
void setVerticalAlignment(Alignment align);
|
void setVerticalAlignment(Alignment align);
|
||||||
void setLineSpacing(float spacing);
|
void setLineSpacing(float spacing);
|
||||||
void setNoTopMargin(bool margin);
|
void setNoTopMargin(bool margin);
|
||||||
void setBackgroundColor(unsigned int color);
|
void setBackgroundColor(unsigned int color);
|
||||||
void setRenderBackground(bool render);
|
void setRenderBackground(bool render) { mRenderBackground = render; }
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
std::string getValue() const override;
|
std::string getValue() const override { return mText; }
|
||||||
void setValue(const std::string& value) override;
|
void setValue(const std::string& value) override { setText(value); }
|
||||||
|
|
||||||
std::string getHiddenValue() const override;
|
std::string getHiddenValue() const override { return mHiddenText; }
|
||||||
void setHiddenValue(const std::string& value) override;
|
void setHiddenValue(const std::string& value) override { setHiddenText(value); }
|
||||||
|
|
||||||
unsigned char getOpacity() const override;
|
unsigned char getOpacity() const override { return mColor & 0x000000FF; }
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
unsigned int getColor() const override { return mColor; };
|
unsigned int getColor() const override { return mColor; }
|
||||||
inline std::shared_ptr<Font> getFont() const override { return mFont; }
|
std::shared_ptr<Font> getFont() const override { return mFont; }
|
||||||
Alignment getHorizontalAlignment() { return mHorizontalAlignment; }
|
Alignment getHorizontalAlignment() { return mHorizontalAlignment; }
|
||||||
Alignment getVerticalAlignment() { return mVerticalAlignment; }
|
Alignment getVerticalAlignment() { return mVerticalAlignment; }
|
||||||
|
|
||||||
|
@ -77,7 +78,6 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculateExtent();
|
void calculateExtent();
|
||||||
|
|
||||||
void onColorChanged();
|
void onColorChanged();
|
||||||
|
|
||||||
unsigned int mColor;
|
unsigned int mColor;
|
||||||
|
|
|
@ -8,24 +8,23 @@
|
||||||
|
|
||||||
#include "components/TextEditComponent.h"
|
#include "components/TextEditComponent.h"
|
||||||
|
|
||||||
#include "resources/Font.h"
|
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#define TEXT_PADDING_HORIZ 10
|
#define TEXT_PADDING_HORIZ 10.0f
|
||||||
#define TEXT_PADDING_VERT 2
|
#define TEXT_PADDING_VERT 2.0f
|
||||||
|
|
||||||
#define CURSOR_REPEAT_START_DELAY 500
|
#define CURSOR_REPEAT_START_DELAY 500
|
||||||
#define CURSOR_REPEAT_SPEED 28 // Lower is faster.
|
#define CURSOR_REPEAT_SPEED 28 // Lower is faster.
|
||||||
|
|
||||||
TextEditComponent::TextEditComponent(
|
TextEditComponent::TextEditComponent(Window* window)
|
||||||
Window* window)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mBox(window, ":/graphics/textinput.svg")
|
||||||
mBox(window, ":/graphics/textinput.svg"),
|
, mFocused(false)
|
||||||
mFocused(false),
|
, mScrollOffset(0.0f, 0.0f)
|
||||||
mScrollOffset(0.0f, 0.0f),
|
, mCursor(0)
|
||||||
mCursor(0), mEditing(false),
|
, mEditing(false)
|
||||||
mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)),
|
, mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT))
|
||||||
mCursorRepeatDir(0)
|
, mCursorRepeatDir(0)
|
||||||
{
|
{
|
||||||
addChild(&mBox);
|
addChild(&mBox);
|
||||||
onFocusLost();
|
onFocusLost();
|
||||||
|
@ -47,8 +46,9 @@ void TextEditComponent::onFocusLost()
|
||||||
|
|
||||||
void TextEditComponent::onSizeChanged()
|
void TextEditComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-34 + mResolutionAdjustment, -32 -
|
mBox.fitTo(mSize, Vector3f::Zero(),
|
||||||
(TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())));
|
Vector2f(-34 + mResolutionAdjustment,
|
||||||
|
-32 - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier())));
|
||||||
onTextChanged(); // Wrap point probably changed.
|
onTextChanged(); // Wrap point probably changed.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,11 +59,6 @@ void TextEditComponent::setValue(const std::string& val)
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextEditComponent::getValue() const
|
|
||||||
{
|
|
||||||
return mText;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextEditComponent::textInput(const std::string& text)
|
void TextEditComponent::textInput(const std::string& text)
|
||||||
{
|
{
|
||||||
if (mEditing) {
|
if (mEditing) {
|
||||||
|
@ -103,33 +98,35 @@ void TextEditComponent::stopEditing()
|
||||||
|
|
||||||
bool TextEditComponent::input(InputConfig* config, Input input)
|
bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
bool const cursor_left = (config->getDeviceId() != DEVICE_KEYBOARD &&
|
bool const cursor_left =
|
||||||
config->isMappedLike("left", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
|
(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("left", input)) ||
|
||||||
input.id == SDLK_LEFT);
|
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_LEFT);
|
||||||
bool const cursor_right = (config->getDeviceId() != DEVICE_KEYBOARD &&
|
bool const cursor_right =
|
||||||
config->isMappedLike("right", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
|
(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("right", input)) ||
|
||||||
input.id == SDLK_RIGHT);
|
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RIGHT);
|
||||||
bool const cursor_up = (config->getDeviceId() != DEVICE_KEYBOARD &&
|
bool const cursor_up =
|
||||||
config->isMappedLike("up", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
|
(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("up", input)) ||
|
||||||
input.id == SDLK_UP);
|
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_UP);
|
||||||
bool const cursor_down = (config->getDeviceId() != DEVICE_KEYBOARD &&
|
bool const cursor_down =
|
||||||
config->isMappedLike("down", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
|
(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("down", input)) ||
|
||||||
input.id == SDLK_DOWN);
|
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_DOWN);
|
||||||
bool const shoulder_left = (config->isMappedLike("leftshoulder", input));
|
bool const shoulder_left = (config->isMappedLike("leftshoulder", input));
|
||||||
bool const shoulder_right = (config->isMappedLike("rightshoulder", input));
|
bool const shoulder_right = (config->isMappedLike("rightshoulder", input));
|
||||||
bool const trigger_left = (config->isMappedLike("lefttrigger", input));
|
bool const trigger_left = (config->isMappedLike("lefttrigger", input));
|
||||||
bool const trigger_right = (config->isMappedLike("righttrigger", input));
|
bool const trigger_right = (config->isMappedLike("righttrigger", input));
|
||||||
|
|
||||||
if (input.value == 0) {
|
if (input.value == 0) {
|
||||||
if (cursor_left || cursor_right || cursor_up || cursor_down ||
|
if (cursor_left || cursor_right || cursor_up || cursor_down || shoulder_left ||
|
||||||
shoulder_left || shoulder_right | trigger_left || trigger_right)
|
shoulder_right | trigger_left || trigger_right) {
|
||||||
mCursorRepeatDir = 0;
|
mCursorRepeatDir = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((config->isMappedTo("a", input) || (config->getDeviceId() == DEVICE_KEYBOARD &&
|
if ((config->isMappedTo("a", input) ||
|
||||||
input.id == SDLK_RETURN)) && mFocused && !mEditing) {
|
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN)) &&
|
||||||
|
mFocused && !mEditing) {
|
||||||
startEditing();
|
startEditing();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -146,8 +143,8 @@ bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
|
|
||||||
// Done editing (accept changes).
|
// Done editing (accept changes).
|
||||||
if ((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) ||
|
if ((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) ||
|
||||||
(config->getDeviceId() != DEVICE_KEYBOARD && (config->isMappedTo("a", input) ||
|
(config->getDeviceId() != DEVICE_KEYBOARD &&
|
||||||
config->isMappedTo("b", input)))) {
|
(config->isMappedTo("a", input) || config->isMappedTo("b", input)))) {
|
||||||
mTextOrig = mText;
|
mTextOrig = mText;
|
||||||
stopEditing();
|
stopEditing();
|
||||||
return true;
|
return true;
|
||||||
|
@ -181,21 +178,22 @@ bool TextEditComponent::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
else if (config->getDeviceId() == DEVICE_KEYBOARD) {
|
else if (config->getDeviceId() == DEVICE_KEYBOARD) {
|
||||||
switch (input.id) {
|
switch (input.id) {
|
||||||
case SDLK_HOME:
|
case SDLK_HOME: {
|
||||||
setCursor(0);
|
setCursor(0);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SDLK_END:
|
case SDLK_END: {
|
||||||
setCursor(std::string::npos);
|
setCursor(std::string::npos);
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case SDLK_DELETE:
|
case SDLK_DELETE: {
|
||||||
if (mCursor < mText.length()) {
|
if (mCursor < mText.length()) {
|
||||||
// Fake as Backspace one char to the right.
|
// Fake as Backspace one char to the right.
|
||||||
moveCursor(1);
|
moveCursor(1);
|
||||||
textInput("\b");
|
textInput("\b");
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Consume all input when editing text.
|
// Consume all input when editing text.
|
||||||
|
@ -241,10 +239,10 @@ void TextEditComponent::setCursor(size_t pos)
|
||||||
|
|
||||||
void TextEditComponent::onTextChanged()
|
void TextEditComponent::onTextChanged()
|
||||||
{
|
{
|
||||||
std::string wrappedText = (isMultiline() ?
|
std::string wrappedText =
|
||||||
mFont->wrapText(mText, getTextAreaSize().x()) : mText);
|
(isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x()) : mText);
|
||||||
mTextCache = std::unique_ptr<TextCache>
|
mTextCache = std::unique_ptr<TextCache>(
|
||||||
(mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity()));
|
mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity()));
|
||||||
|
|
||||||
if (mCursor > static_cast<int>(mText.length()))
|
if (mCursor > static_cast<int>(mText.length()))
|
||||||
mCursor = static_cast<unsigned int>(mText.length());
|
mCursor = static_cast<unsigned int>(mText.length());
|
||||||
|
@ -253,13 +251,14 @@ void TextEditComponent::onTextChanged()
|
||||||
void TextEditComponent::onCursorChanged()
|
void TextEditComponent::onCursorChanged()
|
||||||
{
|
{
|
||||||
if (isMultiline()) {
|
if (isMultiline()) {
|
||||||
Vector2f textSize = mFont->
|
Vector2f textSize =
|
||||||
getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
|
mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
|
||||||
|
|
||||||
if (mScrollOffset.y() + getTextAreaSize().y() < textSize.y() +
|
// Need to scroll down?
|
||||||
mFont->getHeight()) // Need to scroll down?
|
if (mScrollOffset.y() + getTextAreaSize().y() < textSize.y() + mFont->getHeight())
|
||||||
mScrollOffset[1] = textSize.y() - getTextAreaSize().y() + mFont->getHeight();
|
mScrollOffset[1] = textSize.y() - getTextAreaSize().y() + mFont->getHeight();
|
||||||
else if (mScrollOffset.y() > textSize.y()) // Need to scroll up?
|
// Need to scroll up?
|
||||||
|
else if (mScrollOffset.y() > textSize.y())
|
||||||
mScrollOffset[1] = textSize.y();
|
mScrollOffset[1] = textSize.y();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -282,11 +281,11 @@ void TextEditComponent::render(const Transform4x4f& parentTrans)
|
||||||
trans.translation() += Vector3f(getTextAreaPos().x(), getTextAreaPos().y(), 0);
|
trans.translation() += Vector3f(getTextAreaPos().x(), getTextAreaPos().y(), 0);
|
||||||
|
|
||||||
Vector2i clipPos(static_cast<int>(trans.translation().x()),
|
Vector2i clipPos(static_cast<int>(trans.translation().x()),
|
||||||
static_cast<int>(trans.translation().y()));
|
static_cast<int>(trans.translation().y()));
|
||||||
// Use "text area" size for clipping.
|
// Use "text area" size for clipping.
|
||||||
Vector3f dimScaled = trans * Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0);
|
Vector3f dimScaled = trans * Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0);
|
||||||
Vector2i clipDim(static_cast<int>((dimScaled.x()) - trans.translation().x()),
|
Vector2i clipDim(static_cast<int>((dimScaled.x()) - trans.translation().x()),
|
||||||
static_cast<int>((dimScaled.y()) - trans.translation().y()));
|
static_cast<int>((dimScaled.y()) - trans.translation().y()));
|
||||||
Renderer::pushClipRect(clipPos, clipDim);
|
Renderer::pushClipRect(clipPos, clipDim);
|
||||||
|
|
||||||
trans.translate(Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0));
|
trans.translate(Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0));
|
||||||
|
@ -310,28 +309,24 @@ void TextEditComponent::render(const Transform4x4f& parentTrans)
|
||||||
}
|
}
|
||||||
|
|
||||||
float cursorHeight = mFont->getHeight() * 0.8f;
|
float cursorHeight = mFont->getHeight() * 0.8f;
|
||||||
Renderer::drawRect(cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2,
|
Renderer::drawRect(
|
||||||
2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x000000FF, 0x000000FF);
|
cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2.0f,
|
||||||
|
2.0f * Renderer::getScreenWidthModifier(), cursorHeight, 0x000000FF, 0x000000FF);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TextEditComponent::isMultiline()
|
|
||||||
{
|
|
||||||
return (getSize().y() > mFont->getHeight() * 1.25f);
|
|
||||||
}
|
|
||||||
|
|
||||||
Vector2f TextEditComponent::getTextAreaPos() const
|
Vector2f TextEditComponent::getTextAreaPos() const
|
||||||
{
|
{
|
||||||
return Vector2f((-mResolutionAdjustment +
|
return Vector2f(
|
||||||
(TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier())) / 2.0f,
|
(-mResolutionAdjustment + (TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier())) / 2.0f,
|
||||||
(TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()) / 2.0f);
|
(TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()) / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f TextEditComponent::getTextAreaSize() const
|
Vector2f TextEditComponent::getTextAreaSize() const
|
||||||
{
|
{
|
||||||
return Vector2f(mSize.x() + mResolutionAdjustment -
|
return Vector2f(mSize.x() + mResolutionAdjustment -
|
||||||
(TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier()), mSize.y() -
|
(TEXT_PADDING_HORIZ * Renderer::getScreenWidthModifier()),
|
||||||
(TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()));
|
mSize.y() - (TEXT_PADDING_VERT * Renderer::getScreenHeightModifier()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> TextEditComponent::getHelpPrompts()
|
std::vector<HelpPrompt> TextEditComponent::getHelpPrompts()
|
||||||
|
|
|
@ -9,8 +9,9 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
|
#define ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
|
||||||
|
|
||||||
#include "components/NinePatchComponent.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "components/NinePatchComponent.h"
|
||||||
|
#include "resources/Font.h"
|
||||||
|
|
||||||
class Font;
|
class Font;
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
@ -32,13 +33,13 @@ public:
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
void setValue(const std::string& val) override;
|
void setValue(const std::string& val) override;
|
||||||
std::string getValue() const override;
|
std::string getValue() const override { return mText; }
|
||||||
|
|
||||||
void startEditing();
|
void startEditing();
|
||||||
void stopEditing();
|
void stopEditing();
|
||||||
|
|
||||||
inline bool isEditing() const { return mEditing; };
|
bool isEditing() const { return mEditing; }
|
||||||
inline std::shared_ptr<Font> getFont() const override{ return mFont; }
|
std::shared_ptr<Font> getFont() const override { return mFont; }
|
||||||
|
|
||||||
void setCursor(size_t pos);
|
void setCursor(size_t pos);
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ private:
|
||||||
void updateCursorRepeat(int deltaTime);
|
void updateCursorRepeat(int deltaTime);
|
||||||
void moveCursor(int amt);
|
void moveCursor(int amt);
|
||||||
|
|
||||||
bool isMultiline();
|
bool isMultiline() { return (getSize().y() > mFont->getHeight() * 1.25f); }
|
||||||
Vector2f getTextAreaPos() const;
|
Vector2f getTextAreaPos() const;
|
||||||
Vector2f getTextAreaSize() const;
|
Vector2f getTextAreaSize() const;
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
#ifndef ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#ifndef ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
||||||
#define ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#define ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
||||||
|
|
||||||
|
#include "Log.h"
|
||||||
|
#include "Sound.h"
|
||||||
#include "components/IList.h"
|
#include "components/IList.h"
|
||||||
#include "math/Misc.h"
|
#include "math/Misc.h"
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Log.h"
|
|
||||||
#include "Sound.h"
|
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
@ -26,8 +26,7 @@ struct TextListData {
|
||||||
};
|
};
|
||||||
|
|
||||||
// A graphical list. Supports multiple colors for rows and scrolling.
|
// A graphical list. Supports multiple colors for rows and scrolling.
|
||||||
template <typename T>
|
template <typename T> class TextListComponent : public IList<TextListData, T>
|
||||||
class TextListComponent : public IList<TextListData, T>
|
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
using IList<TextListData, T>::mEntries;
|
using IList<TextListData, T>::mEntries;
|
||||||
|
@ -38,9 +37,6 @@ protected:
|
||||||
using IList<TextListData, T>::mSize;
|
using IList<TextListData, T>::mSize;
|
||||||
using IList<TextListData, T>::mCursor;
|
using IList<TextListData, T>::mCursor;
|
||||||
using IList<TextListData, T>::mWindow;
|
using IList<TextListData, T>::mWindow;
|
||||||
// The following change is required for compilation with Clang.
|
|
||||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070
|
|
||||||
// using IList<TextListData, T>::Entry;
|
|
||||||
using IList<TextListData, T>::IList;
|
using IList<TextListData, T>::IList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -53,45 +49,51 @@ public:
|
||||||
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;
|
||||||
void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
void add(const std::string& name, const T& obj, unsigned int colorId);
|
void add(const std::string& name, const T& obj, unsigned int colorId);
|
||||||
|
|
||||||
enum Alignment {
|
enum Alignment {
|
||||||
ALIGN_LEFT,
|
ALIGN_LEFT, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
ALIGN_CENTER,
|
ALIGN_CENTER,
|
||||||
ALIGN_RIGHT
|
ALIGN_RIGHT
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void setAlignment(Alignment align) { mAlignment = align; }
|
void setAlignment(Alignment align) { mAlignment = align; }
|
||||||
|
|
||||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
||||||
{ mCursorChangedCallback = func; }
|
{
|
||||||
|
mCursorChangedCallback = func;
|
||||||
|
}
|
||||||
|
|
||||||
inline void setFont(const std::shared_ptr<Font>& font)
|
void setFont(const std::shared_ptr<Font>& font)
|
||||||
{
|
{
|
||||||
mFont = font;
|
mFont = font;
|
||||||
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
|
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
|
||||||
it->data.textCache.reset();
|
it->data.textCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setUppercase(bool /*uppercase*/)
|
void setUppercase(bool /*uppercase*/)
|
||||||
{
|
{
|
||||||
mUppercase = true;
|
mUppercase = true;
|
||||||
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
|
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
|
||||||
it->data.textCache.reset();
|
it->data.textCache.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void setSelectorHeight(float selectorScale) { mSelectorHeight = selectorScale; }
|
void setSelectorHeight(float selectorScale) { mSelectorHeight = selectorScale; }
|
||||||
inline void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; }
|
void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; }
|
||||||
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
|
void setSelectorColor(unsigned int color) { mSelectorColor = color; }
|
||||||
inline void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; }
|
void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; }
|
||||||
inline void setSelectorColorGradientHorizontal(bool horizontal)
|
void setSelectorColorGradientHorizontal(bool horizontal)
|
||||||
{ mSelectorColorGradientHorizontal = horizontal; }
|
{
|
||||||
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
|
mSelectorColorGradientHorizontal = horizontal;
|
||||||
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
|
}
|
||||||
inline void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; }
|
void setSelectedColor(unsigned int color) { mSelectedColor = color; }
|
||||||
|
void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
|
||||||
|
void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onScroll() override
|
virtual void onScroll() override
|
||||||
|
@ -128,8 +130,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
TextListComponent<T>::TextListComponent(Window* window) :
|
TextListComponent<T>::TextListComponent(Window* window)
|
||||||
IList<TextListData, T>(window), mSelectorImage(window)
|
: IList<TextListData, T>(window)
|
||||||
|
, mSelectorImage(window)
|
||||||
{
|
{
|
||||||
mMarqueeOffset = 0;
|
mMarqueeOffset = 0;
|
||||||
mMarqueeOffset2 = 0;
|
mMarqueeOffset2 = 0;
|
||||||
|
@ -152,8 +155,7 @@ TextListComponent<T>::TextListComponent(Window* window) :
|
||||||
mColors[1] = 0x00FF00FF;
|
mColors[1] = 0x00FF00FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
|
||||||
{
|
{
|
||||||
if (size() == 0)
|
if (size() == 0)
|
||||||
return;
|
return;
|
||||||
|
@ -164,14 +166,14 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
int startEntry = 0;
|
int startEntry = 0;
|
||||||
float y = 0;
|
float y = 0;
|
||||||
|
|
||||||
const float entrySize = std::max(font->getHeight(1.0),
|
const float entrySize =
|
||||||
static_cast<float>(font->getSize())) * mLineSpacing;
|
std::max(font->getHeight(1.0), static_cast<float>(font->getSize())) * mLineSpacing;
|
||||||
|
|
||||||
// Number of entries that can fit on the screen simultaneously.
|
// Number of entries that can fit on the screen simultaneously.
|
||||||
int screenCount = static_cast<int>(mSize.y() / entrySize + 0.5f);
|
int screenCount = static_cast<int>(mSize.y() / entrySize + 0.5f);
|
||||||
|
|
||||||
if (size() >= screenCount) {
|
if (size() >= screenCount) {
|
||||||
startEntry = mCursor - screenCount/2;
|
startEntry = mCursor - screenCount / 2;
|
||||||
if (startEntry < 0)
|
if (startEntry < 0)
|
||||||
startEntry = 0;
|
startEntry = 0;
|
||||||
if (startEntry >= size() - screenCount)
|
if (startEntry >= size() - screenCount)
|
||||||
|
@ -185,36 +187,31 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
// Draw selector bar.
|
// Draw selector bar.
|
||||||
if (startEntry < listCutoff) {
|
if (startEntry < listCutoff) {
|
||||||
if (mSelectorImage.hasImage()) {
|
if (mSelectorImage.hasImage()) {
|
||||||
mSelectorImage.setPosition(0.0f, (mCursor - startEntry) *
|
mSelectorImage.setPosition(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY,
|
||||||
entrySize + mSelectorOffsetY, 0.0f);
|
0.0f);
|
||||||
mSelectorImage.render(trans);
|
mSelectorImage.render(trans);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
Renderer::drawRect(
|
Renderer::drawRect(0.0f, (mCursor - startEntry) * entrySize + mSelectorOffsetY,
|
||||||
0.0f,
|
mSize.x(), mSelectorHeight, mSelectorColor, mSelectorColorEnd,
|
||||||
(mCursor - startEntry) * entrySize + mSelectorOffsetY,
|
mSelectorColorGradientHorizontal);
|
||||||
mSize.x(),
|
|
||||||
mSelectorHeight,
|
|
||||||
mSelectorColor,
|
|
||||||
mSelectorColorEnd,
|
|
||||||
mSelectorColorGradientHorizontal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Settings::getInstance()->getBool("DebugText")) {
|
if (Settings::getInstance()->getBool("DebugText")) {
|
||||||
Renderer::drawRect(mHorizontalMargin, 0.0f, mSize.x() - mHorizontalMargin * 2.0f,
|
Renderer::drawRect(mHorizontalMargin, 0.0f, mSize.x() - mHorizontalMargin * 2.0f, mSize.y(),
|
||||||
mSize.y(), 0x00000033, 0x00000033);
|
0x00000033, 0x00000033);
|
||||||
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x0000FF33, 0x0000FF33);
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x0000FF33, 0x0000FF33);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clip to inside margins.
|
// Clip to inside margins.
|
||||||
Vector3f dim(mSize.x(), mSize.y(), 0.0f);
|
Vector3f dim(mSize.x(), mSize.y(), 0.0f);
|
||||||
dim = trans * dim - trans.translation();
|
dim = trans * dim - trans.translation();
|
||||||
Renderer::pushClipRect(Vector2i(static_cast<int>(trans.translation().x() +
|
Renderer::pushClipRect(
|
||||||
mHorizontalMargin), static_cast<int>(trans.translation().y())),
|
Vector2i(static_cast<int>(trans.translation().x() + mHorizontalMargin),
|
||||||
Vector2i(static_cast<int>(dim.x() - mHorizontalMargin * 2.0f),
|
static_cast<int>(trans.translation().y())),
|
||||||
static_cast<int>(dim.y())));
|
Vector2i(static_cast<int>(dim.x() - mHorizontalMargin * 2.0f), static_cast<int>(dim.y())));
|
||||||
|
|
||||||
for (int i = startEntry; i < listCutoff; i++) {
|
for (int i = startEntry; i < listCutoff; i++) {
|
||||||
typename IList<TextListData, T>::Entry& entry = mEntries.at(static_cast<unsigned int>(i));
|
typename IList<TextListData, T>::Entry& entry = mEntries.at(static_cast<unsigned int>(i));
|
||||||
|
@ -226,9 +223,8 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
color = mColors[entry.data.colorId];
|
color = mColors[entry.data.colorId];
|
||||||
|
|
||||||
if (!entry.data.textCache)
|
if (!entry.data.textCache)
|
||||||
entry.data.textCache = std::unique_ptr<TextCache>(
|
entry.data.textCache = std::unique_ptr<TextCache>(font->buildTextCache(
|
||||||
font->buildTextCache(mUppercase ?
|
mUppercase ? Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
|
||||||
Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
|
|
||||||
|
|
||||||
// If a game is marked as hidden, lower the text opacity a lot.
|
// If a game is marked as hidden, lower the text opacity a lot.
|
||||||
// If a game is marked to not be counted, lower the opacity a moderate amount.
|
// If a game is marked to not be counted, lower the opacity a moderate amount.
|
||||||
|
@ -242,21 +238,21 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
Vector3f offset(0.0f, y, 0.0f);
|
Vector3f offset(0.0f, y, 0.0f);
|
||||||
|
|
||||||
switch (mAlignment) {
|
switch (mAlignment) {
|
||||||
case ALIGN_LEFT:
|
case ALIGN_LEFT:
|
||||||
offset[0] = mHorizontalMargin;
|
|
||||||
break;
|
|
||||||
case ALIGN_CENTER:
|
|
||||||
offset[0] = static_cast<float>((mSize.x() -
|
|
||||||
entry.data.textCache->metrics.size.x()) / 2.0f);
|
|
||||||
if (offset[0] < mHorizontalMargin)
|
|
||||||
offset[0] = mHorizontalMargin;
|
offset[0] = mHorizontalMargin;
|
||||||
break;
|
break;
|
||||||
case ALIGN_RIGHT:
|
case ALIGN_CENTER:
|
||||||
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
|
offset[0] =
|
||||||
offset[0] -= mHorizontalMargin;
|
static_cast<float>((mSize.x() - entry.data.textCache->metrics.size.x()) / 2.0f);
|
||||||
if (offset[0] < mHorizontalMargin)
|
if (offset[0] < mHorizontalMargin)
|
||||||
offset[0] = mHorizontalMargin;
|
offset[0] = mHorizontalMargin;
|
||||||
break;
|
break;
|
||||||
|
case ALIGN_RIGHT:
|
||||||
|
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
|
||||||
|
offset[0] -= mHorizontalMargin;
|
||||||
|
if (offset[0] < mHorizontalMargin)
|
||||||
|
offset[0] = mHorizontalMargin;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render text.
|
// Render text.
|
||||||
|
@ -291,8 +287,7 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> bool TextListComponent<T>::input(InputConfig* config, Input input)
|
||||||
bool TextListComponent<T>::input(InputConfig* config, Input input)
|
|
||||||
{
|
{
|
||||||
if (size() > 0) {
|
if (size() > 0) {
|
||||||
if (input.value != 0) {
|
if (input.value != 0) {
|
||||||
|
@ -324,12 +319,11 @@ bool TextListComponent<T>::input(InputConfig* config, Input input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (config->isMappedLike("down", input) ||
|
if (config->isMappedLike("down", input) || config->isMappedLike("up", input) ||
|
||||||
config->isMappedLike("up", input) ||
|
config->isMappedLike("rightshoulder", input) ||
|
||||||
config->isMappedLike("rightshoulder", input) ||
|
config->isMappedLike("leftshoulder", input) ||
|
||||||
config->isMappedLike("leftshoulder", input) ||
|
config->isMappedLike("lefttrigger", input) ||
|
||||||
config->isMappedLike("lefttrigger", input) ||
|
config->isMappedLike("righttrigger", input))
|
||||||
config->isMappedLike("righttrigger", input))
|
|
||||||
stopScrolling();
|
stopScrolling();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -337,8 +331,7 @@ bool TextListComponent<T>::input(InputConfig* config, Input input)
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> void TextListComponent<T>::update(int deltaTime)
|
||||||
void TextListComponent<T>::update(int deltaTime)
|
|
||||||
{
|
{
|
||||||
listUpdate(deltaTime);
|
listUpdate(deltaTime);
|
||||||
|
|
||||||
|
@ -351,8 +344,10 @@ void TextListComponent<T>::update(int deltaTime)
|
||||||
mMarqueeOffset2 = 0;
|
mMarqueeOffset2 = 0;
|
||||||
|
|
||||||
// If we're not scrolling and this object's text exceeds our size, then marquee it.
|
// If we're not scrolling and this object's text exceeds our size, then marquee it.
|
||||||
const float textLength = mFont->sizeText(Utils::String::toUpper(
|
const float textLength = mFont
|
||||||
mEntries.at(static_cast<unsigned int>(mCursor)).name)).x();
|
->sizeText(Utils::String::toUpper(
|
||||||
|
mEntries.at(static_cast<unsigned int>(mCursor)).name))
|
||||||
|
.x();
|
||||||
const float limit = mSize.x() - mHorizontalMargin * 2.0f;
|
const float limit = mSize.x() - mHorizontalMargin * 2.0f;
|
||||||
|
|
||||||
if (textLength > limit) {
|
if (textLength > limit) {
|
||||||
|
@ -371,7 +366,8 @@ void TextListComponent<T>::update(int deltaTime)
|
||||||
mMarqueeTime -= maxTime;
|
mMarqueeTime -= maxTime;
|
||||||
|
|
||||||
mMarqueeOffset = static_cast<int>(Math::Scroll::loop(delay, scrollTime + returnTime,
|
mMarqueeOffset = static_cast<int>(Math::Scroll::loop(delay, scrollTime + returnTime,
|
||||||
static_cast<float>(mMarqueeTime), scrollLength + returnLength));
|
static_cast<float>(mMarqueeTime),
|
||||||
|
scrollLength + returnLength));
|
||||||
|
|
||||||
if (mMarqueeOffset > (scrollLength - (limit - returnLength)))
|
if (mMarqueeOffset > (scrollLength - (limit - returnLength)))
|
||||||
mMarqueeOffset2 = static_cast<int>(mMarqueeOffset - (scrollLength + returnLength));
|
mMarqueeOffset2 = static_cast<int>(mMarqueeOffset - (scrollLength + returnLength));
|
||||||
|
@ -394,8 +390,7 @@ void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned i
|
||||||
static_cast<IList<TextListData, T>*>(this)->add(entry);
|
static_cast<IList<TextListData, T>*>(this)->add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> void TextListComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
void TextListComponent<T>::onCursorChanged(const CursorState& state)
|
|
||||||
{
|
{
|
||||||
mMarqueeOffset = 0;
|
mMarqueeOffset = 0;
|
||||||
mMarqueeOffset2 = 0;
|
mMarqueeOffset2 = 0;
|
||||||
|
@ -407,7 +402,9 @@ void TextListComponent<T>::onCursorChanged(const CursorState& state)
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& view, const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
GuiComponent::applyTheme(theme, view, element, properties);
|
GuiComponent::applyTheme(theme, view, element, properties);
|
||||||
|
|
||||||
|
@ -424,8 +421,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
if (elem->has("selectorColorEnd"))
|
if (elem->has("selectorColorEnd"))
|
||||||
setSelectorColorEnd(elem->get<unsigned int>("selectorColorEnd"));
|
setSelectorColorEnd(elem->get<unsigned int>("selectorColorEnd"));
|
||||||
if (elem->has("selectorGradientType"))
|
if (elem->has("selectorGradientType"))
|
||||||
setSelectorColorGradientHorizontal(!(elem->get<std::string>
|
setSelectorColorGradientHorizontal(
|
||||||
("selectorGradientType").compare("horizontal")));
|
!(elem->get<std::string>("selectorGradientType").compare("horizontal")));
|
||||||
if (elem->has("selectedColor"))
|
if (elem->has("selectedColor"))
|
||||||
setSelectedColor(elem->get<unsigned int>("selectedColor"));
|
setSelectedColor(elem->get<unsigned int>("selectedColor"));
|
||||||
if (elem->has("primaryColor"))
|
if (elem->has("primaryColor"))
|
||||||
|
@ -435,8 +432,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
setFont(Font::getFromTheme(elem, properties, mFont));
|
||||||
const float selectorHeight = std::max(mFont->getHeight(1.0),
|
const float selectorHeight =
|
||||||
static_cast<float>(mFont->getSize())) * mLineSpacing;
|
std::max(mFont->getHeight(1.0), static_cast<float>(mFont->getSize())) * mLineSpacing;
|
||||||
setSelectorHeight(selectorHeight);
|
setSelectorHeight(selectorHeight);
|
||||||
|
|
||||||
if (properties & ALIGNMENT) {
|
if (properties & ALIGNMENT) {
|
||||||
|
@ -453,8 +450,8 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
}
|
}
|
||||||
if (elem->has("horizontalMargin")) {
|
if (elem->has("horizontalMargin")) {
|
||||||
mHorizontalMargin = elem->get<float>("horizontalMargin") *
|
mHorizontalMargin = elem->get<float>("horizontalMargin") *
|
||||||
(this->mParent ? this->mParent->getSize().x() :
|
(this->mParent ? this->mParent->getSize().x() :
|
||||||
static_cast<float>(Renderer::getScreenWidth()));
|
static_cast<float>(Renderer::getScreenWidth()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -468,7 +465,7 @@ void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
setSelectorHeight(elem->get<float>("selectorHeight") * Renderer::getScreenHeight());
|
setSelectorHeight(elem->get<float>("selectorHeight") * Renderer::getScreenHeight());
|
||||||
if (elem->has("selectorOffsetY")) {
|
if (elem->has("selectorOffsetY")) {
|
||||||
float scale = this->mParent ? this->mParent->getSize().y() :
|
float scale = this->mParent ? this->mParent->getSize().y() :
|
||||||
static_cast<float>(Renderer::getScreenHeight());
|
static_cast<float>(Renderer::getScreenHeight());
|
||||||
setSelectorOffsetY(elem->get<float>("selectorOffsetY") * scale);
|
setSelectorOffsetY(elem->get<float>("selectorOffsetY") * scale);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -8,39 +8,38 @@
|
||||||
|
|
||||||
#include "components/VideoComponent.h"
|
#include "components/VideoComponent.h"
|
||||||
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "utils/FileSystemUtil.h"
|
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "resources/ResourceManager.h"
|
||||||
|
#include "utils/FileSystemUtil.h"
|
||||||
|
|
||||||
#include <SDL2/SDL_timer.h>
|
#include <SDL2/SDL_timer.h>
|
||||||
|
|
||||||
#define SCREENSAVER_FADE_IN_TIME 1100
|
#define SCREENSAVER_FADE_IN_TIME 1100
|
||||||
#define MEDIA_VIEWER_FADE_IN_TIME 600
|
#define MEDIA_VIEWER_FADE_IN_TIME 600
|
||||||
|
|
||||||
VideoComponent::VideoComponent(
|
VideoComponent::VideoComponent(Window* window)
|
||||||
Window* window)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mWindow(window)
|
||||||
mWindow(window),
|
, mStaticImage(window)
|
||||||
mStaticImage(window),
|
, mVideoHeight(0)
|
||||||
mVideoHeight(0),
|
, mVideoWidth(0)
|
||||||
mVideoWidth(0),
|
, mStartDelayed(false)
|
||||||
mStartDelayed(false),
|
, mIsPlaying(false)
|
||||||
mIsPlaying(false),
|
, mIsActuallyPlaying(false)
|
||||||
mIsActuallyPlaying(false),
|
, mPause(false)
|
||||||
mPause(false),
|
, mShowing(false)
|
||||||
mShowing(false),
|
, mDisable(false)
|
||||||
mDisable(false),
|
, mMediaViewerMode(false)
|
||||||
mMediaViewerMode(false),
|
, mScreensaverActive(false)
|
||||||
mScreensaverActive(false),
|
, mScreensaverMode(false)
|
||||||
mScreensaverMode(false),
|
, mGameLaunched(false)
|
||||||
mGameLaunched(false),
|
, mBlockPlayer(false)
|
||||||
mBlockPlayer(false),
|
, mTargetIsMax(false)
|
||||||
mTargetIsMax(false),
|
, mFadeIn(1.0)
|
||||||
mFadeIn(1.0),
|
, mTargetSize(0, 0)
|
||||||
mTargetSize(0, 0),
|
, mVideoAreaPos(0, 0)
|
||||||
mVideoAreaPos(0, 0),
|
, mVideoAreaSize(0, 0)
|
||||||
mVideoAreaSize(0, 0)
|
|
||||||
{
|
{
|
||||||
// Setup the default configuration.
|
// Setup the default configuration.
|
||||||
mConfig.showSnapshotDelay = false;
|
mConfig.showSnapshotDelay = false;
|
||||||
|
@ -79,11 +78,6 @@ bool VideoComponent::setVideo(std::string path)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setDefaultVideo()
|
|
||||||
{
|
|
||||||
setVideo(mConfig.defaultVideoPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::setImage(std::string path)
|
void VideoComponent::setImage(std::string path)
|
||||||
{
|
{
|
||||||
// Check that the image has changed.
|
// Check that the image has changed.
|
||||||
|
@ -94,21 +88,6 @@ void VideoComponent::setImage(std::string path)
|
||||||
mStaticImagePath = path;
|
mStaticImagePath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setScreensaverMode(bool isScreensaver)
|
|
||||||
{
|
|
||||||
mScreensaverMode = isScreensaver;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::setMediaViewerMode(bool isMediaViewer)
|
|
||||||
{
|
|
||||||
mMediaViewerMode = isMediaViewer;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::setOpacity(unsigned char opacity)
|
|
||||||
{
|
|
||||||
mOpacity = opacity;
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::onShow()
|
void VideoComponent::onShow()
|
||||||
{
|
{
|
||||||
mBlockPlayer = false;
|
mBlockPlayer = false;
|
||||||
|
@ -192,21 +171,6 @@ void VideoComponent::topWindow(bool isTop)
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onOriginChanged()
|
|
||||||
{
|
|
||||||
mStaticImage.setOrigin(mOrigin);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::onPositionChanged()
|
|
||||||
{
|
|
||||||
mStaticImage.setPosition(mPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::onSizeChanged()
|
|
||||||
{
|
|
||||||
mStaticImage.onSizeChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoComponent::render(const Transform4x4f& parentTrans)
|
void VideoComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
|
@ -237,19 +201,22 @@ void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans)
|
||||||
// set to start playing the video immediately. Although this may seem a bit inconsistent it
|
// set to start playing the video immediately. Although this may seem a bit inconsistent it
|
||||||
// simply looks better than leaving an empty space where the video would have been located.
|
// simply looks better than leaving an empty space where the video would have been located.
|
||||||
if (mWindow->getGuiStackSize() > 1 || (mConfig.showSnapshotNoVideo && mVideoPath.empty()) ||
|
if (mWindow->getGuiStackSize() > 1 || (mConfig.showSnapshotNoVideo && mVideoPath.empty()) ||
|
||||||
(mStartDelayed && mConfig.showSnapshotDelay)) {
|
(mStartDelayed && mConfig.showSnapshotDelay)) {
|
||||||
mStaticImage.setOpacity(mOpacity);
|
mStaticImage.setOpacity(mOpacity);
|
||||||
mStaticImage.render(parentTrans);
|
mStaticImage.render(parentTrans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties)
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties)
|
||||||
{
|
{
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
GuiComponent::applyTheme(theme, view, element, (properties ^ ThemeFlags::SIZE) |
|
GuiComponent::applyTheme(theme, view, element,
|
||||||
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
|
(properties ^ ThemeFlags::SIZE) |
|
||||||
|
((properties & (ThemeFlags::SIZE | POSITION)) ? ORIGIN : 0));
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video");
|
||||||
|
|
||||||
|
@ -257,8 +224,8 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const s
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Vector2f scale = getParent() ? getParent()->getSize() :
|
Vector2f scale = getParent() ? getParent()->getSize() :
|
||||||
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
Vector2f(static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()));
|
static_cast<float>(Renderer::getScreenHeight()));
|
||||||
|
|
||||||
if (properties & ThemeFlags::SIZE) {
|
if (properties & ThemeFlags::SIZE) {
|
||||||
if (elem->has("size")) {
|
if (elem->has("size")) {
|
||||||
|
@ -308,12 +275,12 @@ void VideoComponent::update(int deltaTime)
|
||||||
// Fade in videos, the time period is a bit different between the screensaver,
|
// Fade in videos, the time period is a bit different between the screensaver,
|
||||||
// media viewer and gamelist view.
|
// media viewer and gamelist view.
|
||||||
if (mScreensaverMode && mFadeIn < 1.0f) {
|
if (mScreensaverMode && mFadeIn < 1.0f) {
|
||||||
mFadeIn = Math::clamp(mFadeIn + (deltaTime /
|
mFadeIn = Math::clamp(mFadeIn + (deltaTime / static_cast<float>(SCREENSAVER_FADE_IN_TIME)),
|
||||||
static_cast<float>(SCREENSAVER_FADE_IN_TIME)), 0.0f, 1.0f);
|
0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else if (mMediaViewerMode && mFadeIn < 1.0f) {
|
else if (mMediaViewerMode && mFadeIn < 1.0f) {
|
||||||
mFadeIn = Math::clamp(mFadeIn + (deltaTime /
|
mFadeIn = Math::clamp(mFadeIn + (deltaTime / static_cast<float>(MEDIA_VIEWER_FADE_IN_TIME)),
|
||||||
static_cast<float>(MEDIA_VIEWER_FADE_IN_TIME)), 0.0f, 1.0f);
|
0.0f, 1.0f);
|
||||||
}
|
}
|
||||||
else if (mFadeIn < 1.0f) {
|
else if (mFadeIn < 1.0f) {
|
||||||
mFadeIn = Math::clamp(mFadeIn + 0.01f, 0.0f, 1.0f);
|
mFadeIn = Math::clamp(mFadeIn + 0.01f, 0.0f, 1.0f);
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
#ifndef ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
#define ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
#include "components/ImageComponent.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -34,15 +34,15 @@ public:
|
||||||
// Loads the video at the given filepath.
|
// Loads the video at the given filepath.
|
||||||
bool setVideo(std::string path);
|
bool setVideo(std::string path);
|
||||||
// Configures the component to show the default video.
|
// Configures the component to show the default video.
|
||||||
void setDefaultVideo();
|
void setDefaultVideo() { setVideo(mConfig.defaultVideoPath); }
|
||||||
// Loads a static image that is displayed if the video cannot be played.
|
// Loads a static image that is displayed if the video cannot be played.
|
||||||
void setImage(std::string path);
|
void setImage(std::string path);
|
||||||
// Sets whether we're in media viewer mode.
|
// Sets whether we're in media viewer mode.
|
||||||
void setMediaViewerMode(bool isMediaViewer);
|
void setMediaViewerMode(bool isMediaViewer) { mMediaViewerMode = isMediaViewer; }
|
||||||
// Sets whether we're in screensaver mode.
|
// Sets whether we're in screensaver mode.
|
||||||
void setScreensaverMode(bool isScreensaver);
|
void setScreensaverMode(bool isScreensaver) { mScreensaverMode = isScreensaver; }
|
||||||
// Set the opacity for the embedded static image.
|
// Set the opacity for the embedded static image.
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override { mOpacity = opacity; }
|
||||||
|
|
||||||
virtual void onShow() override;
|
virtual void onShow() override;
|
||||||
virtual void onHide() override;
|
virtual void onHide() override;
|
||||||
|
@ -57,15 +57,17 @@ public:
|
||||||
virtual void topWindow(bool isTop) override;
|
virtual void topWindow(bool isTop) override;
|
||||||
|
|
||||||
// These functions update the embedded static image.
|
// These functions update the embedded static image.
|
||||||
void onOriginChanged() override;
|
void onOriginChanged() override { mStaticImage.setOrigin(mOrigin); }
|
||||||
void onPositionChanged() override;
|
void onPositionChanged() override { mStaticImage.setPosition(mPosition); }
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override { mStaticImage.onSizeChanged(); }
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
void renderSnapshot(const Transform4x4f& parentTrans);
|
void renderSnapshot(const Transform4x4f& parentTrans);
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
const std::string& element, unsigned int properties) override;
|
const std::string& view,
|
||||||
|
const std::string& element,
|
||||||
|
unsigned int properties) override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
|
@ -76,24 +78,24 @@ public:
|
||||||
// zero, no resizing. This can be set before or after a video is loaded.
|
// zero, no resizing. This can be set before or after a video is loaded.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
virtual void setResize(float width, float height) override = 0;
|
virtual void setResize(float width, float height) override = 0;
|
||||||
inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
||||||
|
|
||||||
// Resize the video to be as large as possible but fit within a box of this size.
|
// Resize the video to be as large as possible but fit within a box of this size.
|
||||||
// This can be set before or after a video is loaded.
|
// This can be set before or after a video is loaded.
|
||||||
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
||||||
virtual void setMaxSize(float width, float height) = 0;
|
virtual void setMaxSize(float width, float height) = 0;
|
||||||
inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
|
void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Start the video immediately.
|
// Start the video immediately.
|
||||||
virtual void startVideo() {};
|
virtual void startVideo() {}
|
||||||
// Stop the video.
|
// Stop the video.
|
||||||
virtual void stopVideo() {};
|
virtual void stopVideo() {}
|
||||||
// Pause the video when a game has been launched.
|
// Pause the video when a game has been launched.
|
||||||
virtual void pauseVideo() {};
|
virtual void pauseVideo() {}
|
||||||
// Handle looping of the video. Must be called periodically.
|
// Handle looping of the video. Must be called periodically.
|
||||||
virtual void handleLooping() {};
|
virtual void handleLooping() {}
|
||||||
virtual void updatePlayer() {};
|
virtual void updatePlayer() {}
|
||||||
|
|
||||||
// Start the video after any configured delay.
|
// Start the video after any configured delay.
|
||||||
void startVideoWithDelay();
|
void startVideoWithDelay();
|
||||||
|
|
|
@ -8,48 +8,44 @@
|
||||||
|
|
||||||
#include "components/VideoFFmpegComponent.h"
|
#include "components/VideoFFmpegComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
|
||||||
#define DEBUG_VIDEO false
|
#define DEBUG_VIDEO false
|
||||||
|
|
||||||
VideoFFmpegComponent::VideoFFmpegComponent(
|
VideoFFmpegComponent::VideoFFmpegComponent(Window* window)
|
||||||
Window* window)
|
: VideoComponent(window)
|
||||||
: VideoComponent(window),
|
, mFrameProcessingThread(nullptr)
|
||||||
mFrameProcessingThread(nullptr),
|
, mFormatContext(nullptr)
|
||||||
mFormatContext(nullptr),
|
, mVideoStream(nullptr)
|
||||||
mVideoStream(nullptr),
|
, mAudioStream(nullptr)
|
||||||
mAudioStream(nullptr),
|
, mVideoCodec(nullptr)
|
||||||
mVideoCodec(nullptr),
|
, mAudioCodec(nullptr)
|
||||||
mAudioCodec(nullptr),
|
, mVideoCodecContext(nullptr)
|
||||||
mVideoCodecContext(nullptr),
|
, mAudioCodecContext(nullptr)
|
||||||
mAudioCodecContext(nullptr),
|
, mVBufferSrcContext(nullptr)
|
||||||
mVBufferSrcContext(nullptr),
|
, mVBufferSinkContext(nullptr)
|
||||||
mVBufferSinkContext(nullptr),
|
, mVFilterGraph(nullptr)
|
||||||
mVFilterGraph(nullptr),
|
, mVFilterInputs(nullptr)
|
||||||
mVFilterInputs(nullptr),
|
, mVFilterOutputs(nullptr)
|
||||||
mVFilterOutputs(nullptr),
|
, mABufferSrcContext(nullptr)
|
||||||
mABufferSrcContext(nullptr),
|
, mABufferSinkContext(nullptr)
|
||||||
mABufferSinkContext(nullptr),
|
, mAFilterGraph(nullptr)
|
||||||
mAFilterGraph(nullptr),
|
, mAFilterInputs(nullptr)
|
||||||
mAFilterInputs(nullptr),
|
, mAFilterOutputs(nullptr)
|
||||||
mAFilterOutputs(nullptr),
|
, mVideoTimeBase(0.0l)
|
||||||
mVideoTimeBase(0.0l),
|
, mVideoTargetQueueSize(0)
|
||||||
mVideoTargetQueueSize(0),
|
, mAudioTargetQueueSize(0)
|
||||||
mAudioTargetQueueSize(0),
|
, mAccumulatedTime(0)
|
||||||
mAccumulatedTime(0),
|
, mStartTimeAccumulation(false)
|
||||||
mStartTimeAccumulation(false),
|
, mDecodedFrame(false)
|
||||||
mDecodedFrame(false),
|
, mEndOfVideo(false)
|
||||||
mEndOfVideo(false)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoFFmpegComponent::~VideoFFmpegComponent()
|
VideoFFmpegComponent::~VideoFFmpegComponent() { stopVideo(); }
|
||||||
{
|
|
||||||
stopVideo();
|
|
||||||
}
|
|
||||||
|
|
||||||
void VideoFFmpegComponent::setResize(float width, float height)
|
void VideoFFmpegComponent::setResize(float width, float height)
|
||||||
{
|
{
|
||||||
|
@ -96,7 +92,6 @@ void VideoFFmpegComponent::resize()
|
||||||
|
|
||||||
mSize[1] = std::round(mSize[1]);
|
mSize[1] = std::round(mSize[1]);
|
||||||
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If both components are set, we just stretch.
|
// If both components are set, we just stretch.
|
||||||
|
@ -127,8 +122,8 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans)
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
if (mDecodedFrame && mFadeIn < 1) {
|
if (mDecodedFrame && mFadeIn < 1) {
|
||||||
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
|
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
|
||||||
color = Renderer::convertRGBAToABGR((fadeIn << 24) |
|
color =
|
||||||
(fadeIn << 16) | (fadeIn << 8) | 255);
|
Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
color = 0xFFFFFFFF;
|
color = 0xFFFFFFFF;
|
||||||
|
@ -139,13 +134,16 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans)
|
||||||
// Render the black rectangle behind the video.
|
// Render the black rectangle behind the video.
|
||||||
if (mVideoRectangleCoords.size() == 4) {
|
if (mVideoRectangleCoords.size() == 4) {
|
||||||
Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
|
Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
|
||||||
mVideoRectangleCoords[2], mVideoRectangleCoords[3], 0x000000FF, 0x000000FF);
|
mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break.
|
||||||
|
0x000000FF, 0x000000FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
|
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
|
||||||
vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
|
vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
|
||||||
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
|
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
|
||||||
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
|
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Round vertices.
|
// Round vertices.
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
|
@ -167,8 +165,8 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans)
|
||||||
int pictureHeight = 0;
|
int pictureHeight = 0;
|
||||||
|
|
||||||
if (pictureSize > 0) {
|
if (pictureSize > 0) {
|
||||||
tempPictureRGBA.insert(tempPictureRGBA.begin(),
|
tempPictureRGBA.insert(tempPictureRGBA.begin(), mOutputPicture.pictureRGBA.begin(),
|
||||||
mOutputPicture.pictureRGBA.begin(), mOutputPicture.pictureRGBA.end());
|
mOutputPicture.pictureRGBA.end());
|
||||||
pictureWidth = mOutputPicture.width;
|
pictureWidth = mOutputPicture.width;
|
||||||
pictureHeight = mOutputPicture.height;
|
pictureHeight = mOutputPicture.height;
|
||||||
|
|
||||||
|
@ -188,14 +186,14 @@ void VideoFFmpegComponent::render(const Transform4x4f& parentTrans)
|
||||||
|
|
||||||
mTexture->bind();
|
mTexture->bind();
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
// Render scanlines if this option is enabled. However, if this is the media viewer
|
// Render scanlines if this option is enabled. However, if this is the media viewer
|
||||||
// or the video screensaver, then skip this as the scanline rendering is then handled
|
// or the video screensaver, then skip this as the scanline rendering is then handled
|
||||||
// in those modules as a postprocessing step.
|
// in those modules as a postprocessing step.
|
||||||
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
||||||
Settings::getInstance()->getBool("GamelistVideoScanlines"))
|
Settings::getInstance()->getBool("GamelistVideoScanlines"))
|
||||||
vertices[0].shaders = Renderer::SHADER_SCANLINES;
|
vertices[0].shaders = Renderer::SHADER_SCANLINES;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Render it.
|
// Render it.
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
@ -215,16 +213,17 @@ void VideoFFmpegComponent::updatePlayer()
|
||||||
mAudioMutex.lock();
|
mAudioMutex.lock();
|
||||||
if (mOutputAudio.size()) {
|
if (mOutputAudio.size()) {
|
||||||
AudioManager::getInstance()->processStream(&mOutputAudio.at(0),
|
AudioManager::getInstance()->processStream(&mOutputAudio.at(0),
|
||||||
static_cast<unsigned int>(mOutputAudio.size()));
|
static_cast<unsigned int>(mOutputAudio.size()));
|
||||||
mOutputAudio.clear();
|
mOutputAudio.clear();
|
||||||
}
|
}
|
||||||
mAudioMutex.unlock();
|
mAudioMutex.unlock();
|
||||||
|
|
||||||
if (mIsActuallyPlaying && mStartTimeAccumulation) {
|
if (mIsActuallyPlaying && mStartTimeAccumulation) {
|
||||||
mAccumulatedTime += static_cast<double>(
|
mAccumulatedTime +=
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>
|
static_cast<double>(std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||||
(std::chrono::high_resolution_clock::now() -
|
std::chrono::high_resolution_clock::now() - mTimeReference)
|
||||||
mTimeReference).count()) / 1000000000.0l;
|
.count()) /
|
||||||
|
1000000000.0l;
|
||||||
}
|
}
|
||||||
|
|
||||||
mTimeReference = std::chrono::high_resolution_clock::now();
|
mTimeReference = std::chrono::high_resolution_clock::now();
|
||||||
|
@ -232,7 +231,7 @@ void VideoFFmpegComponent::updatePlayer()
|
||||||
if (!mFrameProcessingThread) {
|
if (!mFrameProcessingThread) {
|
||||||
AudioManager::getInstance()->unmuteStream();
|
AudioManager::getInstance()->unmuteStream();
|
||||||
mFrameProcessingThread =
|
mFrameProcessingThread =
|
||||||
std::make_unique<std::thread>(&VideoFFmpegComponent::frameProcessing, this);
|
std::make_unique<std::thread>(&VideoFFmpegComponent::frameProcessing, this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +290,7 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
||||||
|
|
||||||
if (!(mVFilterGraph = avfilter_graph_alloc())) {
|
if (!(mVFilterGraph = avfilter_graph_alloc())) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't allocate filter graph";
|
"Couldn't allocate filter graph";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,14 +301,14 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
||||||
const AVFilter* bufferSrc = avfilter_get_by_name("buffer");
|
const AVFilter* bufferSrc = avfilter_get_by_name("buffer");
|
||||||
if (!bufferSrc) {
|
if (!bufferSrc) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't find \"buffer\" filter";
|
"Couldn't find \"buffer\" filter";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVFilter* bufferSink = avfilter_get_by_name("buffersink");
|
const AVFilter* bufferSink = avfilter_get_by_name("buffersink");
|
||||||
if (!bufferSink) {
|
if (!bufferSink) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't find \"buffersink\" filter";
|
"Couldn't find \"buffersink\" filter";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -322,41 +321,30 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
||||||
width += 16 - modulo;
|
width += 16 - modulo;
|
||||||
|
|
||||||
std::string filterArguments =
|
std::string filterArguments =
|
||||||
"width=" + std::to_string(width) + ":" +
|
"width=" + std::to_string(width) + ":" + "height=" + std::to_string(height) +
|
||||||
"height=" + std::to_string(height) +
|
":pix_fmt=" + av_get_pix_fmt_name(mVideoCodecContext->pix_fmt) +
|
||||||
":pix_fmt=" + av_get_pix_fmt_name(mVideoCodecContext->pix_fmt) +
|
":time_base=" + std::to_string(mVideoStream->time_base.num) + "/" +
|
||||||
":time_base=" + std::to_string(mVideoStream->time_base.num) + "/" +
|
std::to_string(mVideoStream->time_base.den) +
|
||||||
std::to_string(mVideoStream->time_base.den) +
|
":sar=" + std::to_string(mVideoCodecContext->sample_aspect_ratio.num) + "/" +
|
||||||
":sar=" + std::to_string(mVideoCodecContext->sample_aspect_ratio.num) + "/" +
|
std::to_string(mVideoCodecContext->sample_aspect_ratio.den);
|
||||||
std::to_string(mVideoCodecContext->sample_aspect_ratio.den);
|
|
||||||
|
|
||||||
returnValue = avfilter_graph_create_filter(
|
returnValue = avfilter_graph_create_filter(&mVBufferSrcContext, bufferSrc, "in",
|
||||||
&mVBufferSrcContext,
|
filterArguments.c_str(), nullptr, mVFilterGraph);
|
||||||
bufferSrc,
|
|
||||||
"in",
|
|
||||||
filterArguments.c_str(),
|
|
||||||
nullptr,
|
|
||||||
mVFilterGraph);
|
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't create filter instance for buffer source: " <<
|
"Couldn't create filter instance for buffer source: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
returnValue = avfilter_graph_create_filter(
|
returnValue = avfilter_graph_create_filter(&mVBufferSinkContext, bufferSink, "out", nullptr,
|
||||||
&mVBufferSinkContext,
|
nullptr, mVFilterGraph);
|
||||||
bufferSink,
|
|
||||||
"out",
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
mVFilterGraph);
|
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't create filter instance for buffer sink: " <<
|
"Couldn't create filter instance for buffer sink: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -376,34 +364,32 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
||||||
// Whether to upscale the frame rate to 60 FPS.
|
// Whether to upscale the frame rate to 60 FPS.
|
||||||
if (Settings::getInstance()->getBool("VideoUpscaleFrameRate")) {
|
if (Settings::getInstance()->getBool("VideoUpscaleFrameRate")) {
|
||||||
if (modulo > 0)
|
if (modulo > 0)
|
||||||
filterDescription =
|
filterDescription = "scale=width=" + std::to_string(width) +
|
||||||
"scale=width=" + std::to_string(width) +
|
":height=" + std::to_string(height) + ",fps=fps=60,";
|
||||||
":height=" + std::to_string(height) +
|
|
||||||
",fps=fps=60,";
|
|
||||||
else
|
else
|
||||||
filterDescription = "fps=fps=60,";
|
filterDescription = "fps=fps=60,";
|
||||||
|
|
||||||
// The "framerate" filter is a more advanced way to upscale the frame rate using
|
// The "framerate" filter is a more advanced way to upscale the frame rate using
|
||||||
// interpolation. However I have not been able to get this to work with slice
|
// interpolation. However I have not been able to get this to work with slice
|
||||||
// threading so the performance is poor. As such it's disabled for now.
|
// threading so the performance is poor. As such it's disabled for now.
|
||||||
// if (modulo > 0)
|
// if (modulo > 0)
|
||||||
// filterDescription =
|
// filterDescription =
|
||||||
// "scale=width=" + std::to_string(width) +
|
// "scale=width=" + std::to_string(width) +
|
||||||
// ":height=" + std::to_string(height) +
|
// ":height=" + std::to_string(height) +
|
||||||
// ",framerate=fps=60,";
|
// ",framerate=fps=60,";
|
||||||
// else
|
// else
|
||||||
// filterDescription = "framerate=fps=60,";
|
// filterDescription = "framerate=fps=60,";
|
||||||
}
|
}
|
||||||
|
|
||||||
filterDescription += "format=pix_fmts=" + std::string(av_get_pix_fmt_name(outPixFormats[0]));
|
filterDescription += "format=pix_fmts=" + std::string(av_get_pix_fmt_name(outPixFormats[0]));
|
||||||
|
|
||||||
returnValue = avfilter_graph_parse_ptr(mVFilterGraph, filterDescription.c_str(),
|
returnValue = avfilter_graph_parse_ptr(mVFilterGraph, filterDescription.c_str(),
|
||||||
&mVFilterInputs, &mVFilterOutputs, nullptr);
|
&mVFilterInputs, &mVFilterOutputs, nullptr);
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't add graph filter: " <<
|
"Couldn't add graph filter: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -411,8 +397,8 @@ bool VideoFFmpegComponent::setupVideoFilters()
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupVideoFilters(): "
|
||||||
"Couldn't configure graph: " <<
|
"Couldn't configure graph: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -431,7 +417,7 @@ bool VideoFFmpegComponent::setupAudioFilters()
|
||||||
|
|
||||||
if (!(mAFilterGraph = avfilter_graph_alloc())) {
|
if (!(mAFilterGraph = avfilter_graph_alloc())) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't allocate filter graph";
|
"Couldn't allocate filter graph";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,55 +428,45 @@ bool VideoFFmpegComponent::setupAudioFilters()
|
||||||
const AVFilter* bufferSrc = avfilter_get_by_name("abuffer");
|
const AVFilter* bufferSrc = avfilter_get_by_name("abuffer");
|
||||||
if (!bufferSrc) {
|
if (!bufferSrc) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't find \"abuffer\" filter";
|
"Couldn't find \"abuffer\" filter";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const AVFilter* bufferSink = avfilter_get_by_name("abuffersink");
|
const AVFilter* bufferSink = avfilter_get_by_name("abuffersink");
|
||||||
if (!bufferSink) {
|
if (!bufferSink) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't find \"abuffersink\" filter";
|
"Couldn't find \"abuffersink\" filter";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char channelLayout[512];
|
char channelLayout[512];
|
||||||
av_get_channel_layout_string(channelLayout, sizeof(channelLayout),
|
av_get_channel_layout_string(channelLayout, sizeof(channelLayout), mAudioCodecContext->channels,
|
||||||
mAudioCodecContext->channels, mAudioCodecContext->channel_layout);
|
mAudioCodecContext->channel_layout);
|
||||||
|
|
||||||
std::string filterArguments =
|
std::string filterArguments =
|
||||||
"time_base=" + std::to_string(mAudioStream->time_base.num) + "/" +
|
"time_base=" + std::to_string(mAudioStream->time_base.num) + "/" +
|
||||||
std::to_string(mAudioStream->time_base.den) +
|
std::to_string(mAudioStream->time_base.den) +
|
||||||
":sample_rate=" + std::to_string(mAudioCodecContext->sample_rate) +
|
":sample_rate=" + std::to_string(mAudioCodecContext->sample_rate) +
|
||||||
":sample_fmt=" + av_get_sample_fmt_name(mAudioCodecContext->sample_fmt) +
|
":sample_fmt=" + av_get_sample_fmt_name(mAudioCodecContext->sample_fmt) +
|
||||||
":channel_layout=" + channelLayout;
|
":channel_layout=" + channelLayout;
|
||||||
|
|
||||||
returnValue = avfilter_graph_create_filter(
|
returnValue = avfilter_graph_create_filter(&mABufferSrcContext, bufferSrc, "in",
|
||||||
&mABufferSrcContext,
|
filterArguments.c_str(), nullptr, mAFilterGraph);
|
||||||
bufferSrc,
|
|
||||||
"in",
|
|
||||||
filterArguments.c_str(),
|
|
||||||
nullptr,
|
|
||||||
mAFilterGraph);
|
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't create filter instance for buffer source: " <<
|
"Couldn't create filter instance for buffer source: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
returnValue = avfilter_graph_create_filter(
|
returnValue = avfilter_graph_create_filter(&mABufferSinkContext, bufferSink, "out", nullptr,
|
||||||
&mABufferSinkContext,
|
nullptr, mAFilterGraph);
|
||||||
bufferSink,
|
|
||||||
"out",
|
|
||||||
nullptr,
|
|
||||||
nullptr,
|
|
||||||
mAFilterGraph);
|
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't create filter instance for buffer sink: " <<
|
"Couldn't create filter instance for buffer sink: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,18 +482,18 @@ bool VideoFFmpegComponent::setupAudioFilters()
|
||||||
mAFilterOutputs->next = nullptr;
|
mAFilterOutputs->next = nullptr;
|
||||||
|
|
||||||
std::string filterDescription =
|
std::string filterDescription =
|
||||||
"aresample=" + std::to_string(outSampleRates[0]) + "," +
|
"aresample=" + std::to_string(outSampleRates[0]) + "," +
|
||||||
"aformat=sample_fmts=" + av_get_sample_fmt_name(outSampleFormats[0]) +
|
"aformat=sample_fmts=" + av_get_sample_fmt_name(outSampleFormats[0]) +
|
||||||
":channel_layouts=stereo,"
|
":channel_layouts=stereo,"
|
||||||
"asetnsamples=n=1024:p=0";
|
"asetnsamples=n=1024:p=0";
|
||||||
|
|
||||||
returnValue = avfilter_graph_parse_ptr(mAFilterGraph, filterDescription.c_str(),
|
returnValue = avfilter_graph_parse_ptr(mAFilterGraph, filterDescription.c_str(),
|
||||||
&mAFilterInputs, &mAFilterOutputs, nullptr);
|
&mAFilterInputs, &mAFilterOutputs, nullptr);
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't add graph filter: " <<
|
"Couldn't add graph filter: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -525,8 +501,8 @@ bool VideoFFmpegComponent::setupAudioFilters()
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
LOG(LogError) << "VideoFFmpegComponent::setupAudioFilters(): "
|
||||||
"Couldn't configure graph: " <<
|
"Couldn't configure graph: "
|
||||||
av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
<< av_make_error_string(errorMessage, sizeof(errorMessage), returnValue);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,20 +520,20 @@ void VideoFFmpegComponent::readFrames()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (mVideoCodecContext && mFormatContext) {
|
if (mVideoCodecContext && mFormatContext) {
|
||||||
if (mVideoFrameQueue.size() < mVideoTargetQueueSize || (mAudioStreamIndex >= 0 &&
|
if (mVideoFrameQueue.size() < mVideoTargetQueueSize ||
|
||||||
mAudioFrameQueue.size() < mAudioTargetQueueSize)) {
|
(mAudioStreamIndex >= 0 && mAudioFrameQueue.size() < mAudioTargetQueueSize)) {
|
||||||
while ((readFrameReturn = av_read_frame(mFormatContext, mPacket)) >= 0) {
|
while ((readFrameReturn = av_read_frame(mFormatContext, mPacket)) >= 0) {
|
||||||
if (mPacket->stream_index == mVideoStreamIndex) {
|
if (mPacket->stream_index == mVideoStreamIndex) {
|
||||||
if (!avcodec_send_packet(mVideoCodecContext, mPacket) &&
|
if (!avcodec_send_packet(mVideoCodecContext, mPacket) &&
|
||||||
!avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) {
|
!avcodec_receive_frame(mVideoCodecContext, mVideoFrame)) {
|
||||||
|
|
||||||
// We have a video frame that needs conversion to RGBA format.
|
// We have a video frame that needs conversion to RGBA format.
|
||||||
int returnValue = av_buffersrc_add_frame_flags(mVBufferSrcContext,
|
int returnValue = av_buffersrc_add_frame_flags(
|
||||||
mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
mVBufferSrcContext, mVideoFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
|
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
|
||||||
"Couldn't add video frame to buffer source";
|
"Couldn't add video frame to buffer source";
|
||||||
}
|
}
|
||||||
|
|
||||||
av_packet_unref(mPacket);
|
av_packet_unref(mPacket);
|
||||||
|
@ -566,15 +542,15 @@ void VideoFFmpegComponent::readFrames()
|
||||||
}
|
}
|
||||||
else if (mPacket->stream_index == mAudioStreamIndex) {
|
else if (mPacket->stream_index == mAudioStreamIndex) {
|
||||||
if (!avcodec_send_packet(mAudioCodecContext, mPacket) &&
|
if (!avcodec_send_packet(mAudioCodecContext, mPacket) &&
|
||||||
!avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) {
|
!avcodec_receive_frame(mAudioCodecContext, mAudioFrame)) {
|
||||||
|
|
||||||
// We have an audio frame that needs conversion and resampling.
|
// We have an audio frame that needs conversion and resampling.
|
||||||
int returnValue = av_buffersrc_add_frame_flags(mABufferSrcContext,
|
int returnValue = av_buffersrc_add_frame_flags(
|
||||||
mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
mABufferSrcContext, mAudioFrame, AV_BUFFERSRC_FLAG_KEEP_REF);
|
||||||
|
|
||||||
if (returnValue < 0) {
|
if (returnValue < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
|
LOG(LogError) << "VideoFFmpegComponent::readFrames(): "
|
||||||
"Couldn't add audio frame to buffer source";
|
"Couldn't add audio frame to buffer source";
|
||||||
}
|
}
|
||||||
|
|
||||||
av_packet_unref(mPacket);
|
av_packet_unref(mPacket);
|
||||||
|
@ -606,21 +582,20 @@ void VideoFFmpegComponent::getProcessedFrames()
|
||||||
// (picture) should be displayed. The packet DTS value is used for the basis of
|
// (picture) should be displayed. The packet DTS value is used for the basis of
|
||||||
// the calculation as per the recommendation in the FFmpeg documentation for
|
// the calculation as per the recommendation in the FFmpeg documentation for
|
||||||
// the av_read_frame function.
|
// the av_read_frame function.
|
||||||
double pts = static_cast<double>(mVideoFrameResampled->pkt_dts) *
|
double pts =
|
||||||
av_q2d(mVideoStream->time_base);
|
static_cast<double>(mVideoFrameResampled->pkt_dts) * av_q2d(mVideoStream->time_base);
|
||||||
|
|
||||||
// Needs to be adjusted if changing the rate?
|
// Needs to be adjusted if changing the rate?
|
||||||
double frameDuration = static_cast<double>(mVideoFrameResampled->pkt_duration) *
|
double frameDuration = static_cast<double>(mVideoFrameResampled->pkt_duration) *
|
||||||
av_q2d(mVideoStream->time_base);
|
av_q2d(mVideoStream->time_base);
|
||||||
|
|
||||||
currFrame.pts = pts;
|
currFrame.pts = pts;
|
||||||
currFrame.frameDuration = frameDuration;
|
currFrame.frameDuration = frameDuration;
|
||||||
|
|
||||||
int bufferSize = mVideoFrameResampled->width * mVideoFrameResampled->height * 4;
|
int bufferSize = mVideoFrameResampled->width * mVideoFrameResampled->height * 4;
|
||||||
|
|
||||||
currFrame.frameRGBA.insert(currFrame.frameRGBA.begin(),
|
currFrame.frameRGBA.insert(currFrame.frameRGBA.begin(), &mVideoFrameResampled->data[0][0],
|
||||||
&mVideoFrameResampled->data[0][0],
|
&mVideoFrameResampled->data[0][bufferSize]);
|
||||||
&mVideoFrameResampled->data[0][bufferSize]);
|
|
||||||
|
|
||||||
mVideoFrameQueue.push(currFrame);
|
mVideoFrameQueue.push(currFrame);
|
||||||
av_frame_unref(mVideoFrameResampled);
|
av_frame_unref(mVideoFrameResampled);
|
||||||
|
@ -629,8 +604,8 @@ void VideoFFmpegComponent::getProcessedFrames()
|
||||||
// Audio frames.
|
// Audio frames.
|
||||||
// When resampling, we may not always get a frame returned from the sink as there may not
|
// When resampling, we may not always get a frame returned from the sink as there may not
|
||||||
// have been enough data available to the filter.
|
// have been enough data available to the filter.
|
||||||
while (mAudioCodecContext && av_buffersink_get_frame(mABufferSinkContext,
|
while (mAudioCodecContext &&
|
||||||
mAudioFrameResampled) >= 0) {
|
av_buffersink_get_frame(mABufferSinkContext, mAudioFrameResampled) >= 0) {
|
||||||
|
|
||||||
AudioFrame currFrame;
|
AudioFrame currFrame;
|
||||||
AVRational timeBase;
|
AVRational timeBase;
|
||||||
|
@ -644,11 +619,11 @@ void VideoFFmpegComponent::getProcessedFrames()
|
||||||
currFrame.pts = pts;
|
currFrame.pts = pts;
|
||||||
|
|
||||||
int bufferSize = mAudioFrameResampled->nb_samples * mAudioFrameResampled->channels *
|
int bufferSize = mAudioFrameResampled->nb_samples * mAudioFrameResampled->channels *
|
||||||
av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT);
|
av_get_bytes_per_sample(AV_SAMPLE_FMT_FLT);
|
||||||
|
|
||||||
currFrame.resampledData.insert(currFrame.resampledData.begin(),
|
currFrame.resampledData.insert(currFrame.resampledData.begin(),
|
||||||
&mAudioFrameResampled->data[0][0],
|
&mAudioFrameResampled->data[0][0],
|
||||||
&mAudioFrameResampled->data[0][bufferSize]);
|
&mAudioFrameResampled->data[0][bufferSize]);
|
||||||
|
|
||||||
mAudioFrameQueue.push(currFrame);
|
mAudioFrameQueue.push(currFrame);
|
||||||
av_frame_unref(mAudioFrameResampled);
|
av_frame_unref(mAudioFrameResampled);
|
||||||
|
@ -674,22 +649,21 @@ void VideoFFmpegComponent::outputFrames()
|
||||||
if (mAudioFrameQueue.front().pts < mAccumulatedTime + AUDIO_BUFFER) {
|
if (mAudioFrameQueue.front().pts < mAccumulatedTime + AUDIO_BUFFER) {
|
||||||
// Enable only when needed, as this generates a lot of debug output.
|
// Enable only when needed, as this generates a lot of debug output.
|
||||||
if (DEBUG_VIDEO) {
|
if (DEBUG_VIDEO) {
|
||||||
LOG(LogDebug) << "Processing audio frame with PTS: " <<
|
LOG(LogDebug) << "Processing audio frame with PTS: "
|
||||||
mAudioFrameQueue.front().pts;
|
<< mAudioFrameQueue.front().pts;
|
||||||
LOG(LogDebug) << "Total audio frames processed / audio frame queue size: " <<
|
LOG(LogDebug) << "Total audio frames processed / audio frame queue size: "
|
||||||
mAudioFrameCount << " / " << std::to_string(mAudioFrameQueue.size());
|
<< mAudioFrameCount << " / "
|
||||||
|
<< std::to_string(mAudioFrameQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool outputSound = false;
|
bool outputSound = false;
|
||||||
|
|
||||||
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
||||||
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mScreensaverMode && Settings::getInstance()->
|
else if (mScreensaverMode && Settings::getInstance()->getBool("ScreensaverVideoAudio"))
|
||||||
getBool("ScreensaverVideoAudio"))
|
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mMediaViewerMode && Settings::getInstance()->
|
else if (mMediaViewerMode && Settings::getInstance()->getBool("MediaViewerVideoAudio"))
|
||||||
getBool("MediaViewerVideoAudio"))
|
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
|
|
||||||
if (outputSound) {
|
if (outputSound) {
|
||||||
|
@ -697,8 +671,8 @@ void VideoFFmpegComponent::outputFrames()
|
||||||
mAudioMutex.lock();
|
mAudioMutex.lock();
|
||||||
|
|
||||||
mOutputAudio.insert(mOutputAudio.end(),
|
mOutputAudio.insert(mOutputAudio.end(),
|
||||||
mAudioFrameQueue.front().resampledData.begin(),
|
mAudioFrameQueue.front().resampledData.begin(),
|
||||||
mAudioFrameQueue.front().resampledData.end());
|
mAudioFrameQueue.front().resampledData.end());
|
||||||
|
|
||||||
mAudioMutex.unlock();
|
mAudioMutex.unlock();
|
||||||
}
|
}
|
||||||
|
@ -717,10 +691,11 @@ void VideoFFmpegComponent::outputFrames()
|
||||||
if (mVideoFrameQueue.front().pts < mAccumulatedTime) {
|
if (mVideoFrameQueue.front().pts < mAccumulatedTime) {
|
||||||
// Enable only when needed, as this generates a lot of debug output.
|
// Enable only when needed, as this generates a lot of debug output.
|
||||||
if (DEBUG_VIDEO) {
|
if (DEBUG_VIDEO) {
|
||||||
LOG(LogDebug) << "Processing video frame with PTS: " <<
|
LOG(LogDebug) << "Processing video frame with PTS: "
|
||||||
mVideoFrameQueue.front().pts;
|
<< mVideoFrameQueue.front().pts;
|
||||||
LOG(LogDebug) << "Total video frames processed / video frame queue size: " <<
|
LOG(LogDebug) << "Total video frames processed / video frame queue size: "
|
||||||
mVideoFrameCount << " / " << std::to_string(mVideoFrameQueue.size());
|
<< mVideoFrameCount << " / "
|
||||||
|
<< std::to_string(mVideoFrameQueue.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
mPictureMutex.lock();
|
mPictureMutex.lock();
|
||||||
|
@ -734,7 +709,7 @@ void VideoFFmpegComponent::outputFrames()
|
||||||
// rates close to, or at, the rendering frame rate, for example 59.94 and 60 FPS.
|
// rates close to, or at, the rendering frame rate, for example 59.94 and 60 FPS.
|
||||||
if (mDecodedFrame && !mOutputPicture.hasBeenRendered) {
|
if (mDecodedFrame && !mOutputPicture.hasBeenRendered) {
|
||||||
double timeDifference = mAccumulatedTime - mVideoFrameQueue.front().pts -
|
double timeDifference = mAccumulatedTime - mVideoFrameQueue.front().pts -
|
||||||
mVideoFrameQueue.front().frameDuration * 2.0l;
|
mVideoFrameQueue.front().frameDuration * 2.0l;
|
||||||
if (timeDifference < mVideoFrameQueue.front().frameDuration) {
|
if (timeDifference < mVideoFrameQueue.front().frameDuration) {
|
||||||
mPictureMutex.unlock();
|
mPictureMutex.unlock();
|
||||||
break;
|
break;
|
||||||
|
@ -743,8 +718,8 @@ void VideoFFmpegComponent::outputFrames()
|
||||||
|
|
||||||
mOutputPicture.pictureRGBA.clear();
|
mOutputPicture.pictureRGBA.clear();
|
||||||
mOutputPicture.pictureRGBA.insert(mOutputPicture.pictureRGBA.begin(),
|
mOutputPicture.pictureRGBA.insert(mOutputPicture.pictureRGBA.begin(),
|
||||||
mVideoFrameQueue.front().frameRGBA.begin(),
|
mVideoFrameQueue.front().frameRGBA.begin(),
|
||||||
mVideoFrameQueue.front().frameRGBA.end());
|
mVideoFrameQueue.front().frameRGBA.end());
|
||||||
|
|
||||||
mOutputPicture.width = mVideoFrameQueue.front().width;
|
mOutputPicture.width = mVideoFrameQueue.front().width;
|
||||||
mOutputPicture.height = mVideoFrameQueue.front().height;
|
mOutputPicture.height = mVideoFrameQueue.front().height;
|
||||||
|
@ -802,10 +777,10 @@ void VideoFFmpegComponent::calculateBlackRectangle()
|
||||||
rectHeight = mSize.y();
|
rectHeight = mSize.y();
|
||||||
}
|
}
|
||||||
// Populate the rectangle coordinates to be used in render().
|
// Populate the rectangle coordinates to be used in render().
|
||||||
mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.x() -
|
mVideoRectangleCoords.push_back(
|
||||||
rectWidth * mOrigin.x()));
|
std::round(mVideoAreaPos.x() - rectWidth * mOrigin.x()));
|
||||||
mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.y() -
|
mVideoRectangleCoords.push_back(
|
||||||
rectHeight * mOrigin.y()));
|
std::round(mVideoAreaPos.y() - rectHeight * mOrigin.y()));
|
||||||
mVideoRectangleCoords.push_back(std::round(rectWidth));
|
mVideoRectangleCoords.push_back(std::round(rectWidth));
|
||||||
mVideoRectangleCoords.push_back(std::round(rectHeight));
|
mVideoRectangleCoords.push_back(std::round(rectHeight));
|
||||||
}
|
}
|
||||||
|
@ -852,14 +827,16 @@ void VideoFFmpegComponent::startVideo()
|
||||||
// File operations and basic setup.
|
// File operations and basic setup.
|
||||||
|
|
||||||
if (avformat_open_input(&mFormatContext, filePath.c_str(), nullptr, nullptr)) {
|
if (avformat_open_input(&mFormatContext, filePath.c_str(), nullptr, nullptr)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't open video file \"" <<
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
mVideoPath << "\"";
|
"Couldn't open video file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avformat_find_stream_info(mFormatContext, nullptr)) {
|
if (avformat_find_stream_info(mFormatContext, nullptr)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't read stream information"
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"from video file \"" << mVideoPath << "\"";
|
"Couldn't read stream information from video file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -868,12 +845,13 @@ void VideoFFmpegComponent::startVideo()
|
||||||
|
|
||||||
// Video stream setup.
|
// Video stream setup.
|
||||||
|
|
||||||
mVideoStreamIndex = av_find_best_stream(
|
mVideoStreamIndex =
|
||||||
mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
av_find_best_stream(mFormatContext, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
|
||||||
|
|
||||||
if (mVideoStreamIndex < 0) {
|
if (mVideoStreamIndex < 0) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't retrieve video stream "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"for file \"" << mVideoPath << "\"";
|
"Couldn't retrieve video stream for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,16 +862,18 @@ void VideoFFmpegComponent::startVideo()
|
||||||
mVideoCodec = const_cast<AVCodec*>(avcodec_find_decoder(mVideoStream->codecpar->codec_id));
|
mVideoCodec = const_cast<AVCodec*>(avcodec_find_decoder(mVideoStream->codecpar->codec_id));
|
||||||
|
|
||||||
if (!mVideoCodec) {
|
if (!mVideoCodec) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't find a suitable video "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"codec for file \"" << mVideoPath << "\"";
|
"Couldn't find a suitable video codec for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mVideoCodecContext = avcodec_alloc_context3(mVideoCodec);
|
mVideoCodecContext = avcodec_alloc_context3(mVideoCodec);
|
||||||
|
|
||||||
if (!mVideoCodec) {
|
if (!mVideoCodec) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't allocate video "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"codec context for file \"" << mVideoPath << "\"";
|
"Couldn't allocate video codec context for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -901,35 +881,38 @@ void VideoFFmpegComponent::startVideo()
|
||||||
mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED;
|
mVideoCodecContext->flags |= AV_CODEC_FLAG_TRUNCATED;
|
||||||
|
|
||||||
if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) {
|
if (avcodec_parameters_to_context(mVideoCodecContext, mVideoStream->codecpar)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't fill the video "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"codec context parameters for file \"" << mVideoPath << "\"";
|
"Couldn't fill the video codec context parameters for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) {
|
if (avcodec_open2(mVideoCodecContext, mVideoCodec, nullptr)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't initialize the "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"video codec context for file \"" << mVideoPath << "\"";
|
"Couldn't initialize the video codec context for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Audio stream setup, optional as some videos may not have any audio tracks.
|
// Audio stream setup, optional as some videos may not have any audio tracks.
|
||||||
|
|
||||||
mAudioStreamIndex = av_find_best_stream(
|
mAudioStreamIndex =
|
||||||
mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
|
av_find_best_stream(mFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
|
||||||
|
|
||||||
if (mAudioStreamIndex < 0) {
|
if (mAudioStreamIndex < 0) {
|
||||||
LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): Couldn't retrieve audio stream "
|
LOG(LogDebug) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"for file \"" << mVideoPath << "\"";
|
"Couldn't retrieve audio stream for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mAudioStreamIndex >= 0) {
|
if (mAudioStreamIndex >= 0) {
|
||||||
mAudioStream = mFormatContext->streams[mAudioStreamIndex];
|
mAudioStream = mFormatContext->streams[mAudioStreamIndex];
|
||||||
mAudioCodec = const_cast<AVCodec*>(
|
mAudioCodec =
|
||||||
avcodec_find_decoder(mAudioStream->codecpar->codec_id));
|
const_cast<AVCodec*>(avcodec_find_decoder(mAudioStream->codecpar->codec_id));
|
||||||
|
|
||||||
if (!mAudioCodec) {
|
if (!mAudioCodec) {
|
||||||
LOG(LogError) << "Couldn't find a suitable audio codec for file \"" <<
|
LOG(LogError) << "Couldn't find a suitable audio codec for file \"" << mVideoPath
|
||||||
mVideoPath << "\"";
|
<< "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -943,14 +926,16 @@ void VideoFFmpegComponent::startVideo()
|
||||||
mAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
mAudioCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
|
||||||
|
|
||||||
if (avcodec_parameters_to_context(mAudioCodecContext, mAudioStream->codecpar)) {
|
if (avcodec_parameters_to_context(mAudioCodecContext, mAudioStream->codecpar)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't fill the audio "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"codec context parameters for file \"" << mVideoPath << "\"";
|
"Couldn't fill the audio codec context parameters for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (avcodec_open2(mAudioCodecContext, mAudioCodec, nullptr)) {
|
if (avcodec_open2(mAudioCodecContext, mAudioCodec, nullptr)) {
|
||||||
LOG(LogError) << "VideoFFmpegComponent::startVideo(): Couldn't initialize the "
|
LOG(LogError) << "VideoFFmpegComponent::startVideo(): "
|
||||||
"audio codec context for file \"" << mVideoPath << "\"";
|
"Couldn't initialize the audio codec context for file \""
|
||||||
|
<< mVideoPath << "\"";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -959,7 +944,7 @@ void VideoFFmpegComponent::startVideo()
|
||||||
|
|
||||||
// Set some reasonable target queue sizes (buffers).
|
// Set some reasonable target queue sizes (buffers).
|
||||||
mVideoTargetQueueSize = static_cast<int>(av_q2d(mVideoStream->avg_frame_rate) / 2.0l);
|
mVideoTargetQueueSize = static_cast<int>(av_q2d(mVideoStream->avg_frame_rate) / 2.0l);
|
||||||
if (mAudioStreamIndex >=0)
|
if (mAudioStreamIndex >= 0)
|
||||||
mAudioTargetQueueSize = mAudioStream->codecpar->channels * 15;
|
mAudioTargetQueueSize = mAudioStream->codecpar->channels * 15;
|
||||||
else
|
else
|
||||||
mAudioTargetQueueSize = 30;
|
mAudioTargetQueueSize = 30;
|
||||||
|
@ -1035,7 +1020,7 @@ void VideoFFmpegComponent::handleLooping()
|
||||||
// If the screensaver video swap time is set to 0, it means we should
|
// If the screensaver video swap time is set to 0, it means we should
|
||||||
// skip to the next game when the video has finished playing.
|
// skip to the next game when the video has finished playing.
|
||||||
if (mScreensaverMode &&
|
if (mScreensaverMode &&
|
||||||
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) {
|
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) {
|
||||||
mWindow->screensaverTriggerNextGame();
|
mWindow->screensaverTriggerNextGame();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
|
@ -14,8 +14,7 @@
|
||||||
|
|
||||||
#include "VideoComponent.h"
|
#include "VideoComponent.h"
|
||||||
|
|
||||||
extern "C"
|
extern "C" {
|
||||||
{
|
|
||||||
#include <libavcodec/avcodec.h>
|
#include <libavcodec/avcodec.h>
|
||||||
#include <libavfilter/avfilter.h>
|
#include <libavfilter/avfilter.h>
|
||||||
#include <libavfilter/buffersink.h>
|
#include <libavfilter/buffersink.h>
|
||||||
|
@ -89,8 +88,8 @@ private:
|
||||||
AVFormatContext* mFormatContext;
|
AVFormatContext* mFormatContext;
|
||||||
AVStream* mVideoStream;
|
AVStream* mVideoStream;
|
||||||
AVStream* mAudioStream;
|
AVStream* mAudioStream;
|
||||||
AVCodec *mVideoCodec;
|
AVCodec* mVideoCodec;
|
||||||
AVCodec *mAudioCodec;
|
AVCodec* mAudioCodec;
|
||||||
AVCodecContext* mVideoCodecContext;
|
AVCodecContext* mVideoCodecContext;
|
||||||
AVCodecContext* mAudioCodecContext;
|
AVCodecContext* mAudioCodecContext;
|
||||||
int mVideoStreamIndex;
|
int mVideoStreamIndex;
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
#if defined(_RPI_)
|
#if defined(_RPI_)
|
||||||
#include "components/VideoOmxComponent.h"
|
#include "components/VideoOmxComponent.h"
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
@ -21,18 +21,19 @@
|
||||||
class VolumeControl
|
class VolumeControl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<VolumeControl> & getInstance();
|
static std::shared_ptr<VolumeControl>& getInstance();
|
||||||
int getVolume() const;
|
int getVolume() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
VideoOmxComponent::VideoOmxComponent(Window* window) :
|
VideoOmxComponent::VideoOmxComponent(Window* window)
|
||||||
VideoComponent(window),
|
: VideoComponent(window)
|
||||||
mPlayerPid(-1)
|
, mPlayerPid(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoOmxComponent::~VideoOmxComponent()
|
VideoOmxComponent::~VideoOmxComponent()
|
||||||
{
|
{
|
||||||
|
// Stop video when destroyed.
|
||||||
stopVideo();
|
stopVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -79,8 +80,8 @@ void VideoOmxComponent::startVideo()
|
||||||
mPlayingVideoPath = mVideoPath;
|
mPlayingVideoPath = mVideoPath;
|
||||||
|
|
||||||
// Disable AudioManager so video can play, in case we're requesting ALSA.
|
// Disable AudioManager so video can play, in case we're requesting ALSA.
|
||||||
if (Utils::String::startsWith(Settings::getInstance()->
|
if (Utils::String::startsWith(Settings::getInstance()->getString("OMXAudioDev").c_str(),
|
||||||
getString("OMXAudioDev").c_str(), "alsa"))
|
"alsa"))
|
||||||
AudioManager::getInstance()->deinit();
|
AudioManager::getInstance()->deinit();
|
||||||
|
|
||||||
// Start the player process.
|
// Start the player process.
|
||||||
|
@ -112,66 +113,84 @@ void VideoOmxComponent::startVideo()
|
||||||
const int x2 = static_cast<int>(x1 + mSize.x());
|
const int x2 = static_cast<int>(x1 + mSize.x());
|
||||||
const int y2 = static_cast<int>(y1 + mSize.y());
|
const int y2 = static_cast<int>(y1 + mSize.y());
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 1: {
|
case 1: {
|
||||||
const int x1 = static_cast<int>(Renderer::getWindowWidth() -
|
const int x1 =
|
||||||
Renderer::getScreenOffsetY() - y - mSize.y());
|
static_cast<int>(Renderer::getWindowWidth() -
|
||||||
|
Renderer::getScreenOffsetY() - y - mSize.y());
|
||||||
const int y1 = static_cast<int>(Renderer::getScreenOffsetX() + x);
|
const int y1 = static_cast<int>(Renderer::getScreenOffsetX() + x);
|
||||||
const int x2 = static_cast<int>(x1 + mSize.y());
|
const int x2 = static_cast<int>(x1 + mSize.y());
|
||||||
const int y2 = static_cast<int>(y1 + mSize.x());
|
const int y2 = static_cast<int>(y1 + mSize.x());
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 2: {
|
case 2: {
|
||||||
const int x1 = static_cast<int>(Renderer::getWindowWidth() -
|
const int x1 =
|
||||||
Renderer::getScreenOffsetX() - x - mSize.x());
|
static_cast<int>(Renderer::getWindowWidth() -
|
||||||
const int y1 = static_cast<int>(Renderer::getWindowHeight() -
|
Renderer::getScreenOffsetX() - x - mSize.x());
|
||||||
Renderer::getScreenOffsetY() - y - mSize.y());
|
const int y1 =
|
||||||
|
static_cast<int>(Renderer::getWindowHeight() -
|
||||||
|
Renderer::getScreenOffsetY() - y - mSize.y());
|
||||||
const int x2 = static_cast<int>(x1 + mSize.x());
|
const int x2 = static_cast<int>(x1 + mSize.x());
|
||||||
const int y2 = static_cast<int>(y1 + mSize.y());
|
const int y2 = static_cast<int>(y1 + mSize.y());
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
|
|
||||||
case 3: {
|
case 3: {
|
||||||
const int x1 = static_cast<int>(Renderer::getScreenOffsetY() + y);
|
const int x1 = static_cast<int>(Renderer::getScreenOffsetY() + y);
|
||||||
const int y1 = static_cast<int>(Renderer::getWindowHeight() -
|
const int y1 =
|
||||||
Renderer::getScreenOffsetX() - x - mSize.x());
|
static_cast<int>(Renderer::getWindowHeight() -
|
||||||
|
Renderer::getScreenOffsetX() - x - mSize.x());
|
||||||
const int x2 = static_cast<int>(x1 + mSize.y());
|
const int x2 = static_cast<int>(x1 + mSize.y());
|
||||||
const int y2 = static_cast<int>(y1 + mSize.x());
|
const int y2 = static_cast<int>(y1 + mSize.x());
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
}
|
} break;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate the video.
|
// Rotate the video.
|
||||||
switch (Renderer::getScreenRotate()) {
|
switch (Renderer::getScreenRotate()) {
|
||||||
case 0: { sprintf(buf2, "%d", static_cast<int>(0)); } break;
|
case 0: {
|
||||||
case 1: { sprintf(buf2, "%d", static_cast<int>(90)); } break;
|
sprintf(buf2, "%d", static_cast<int>(0));
|
||||||
case 2: { sprintf(buf2, "%d", static_cast<int>(180)); } break;
|
} break;
|
||||||
case 3: { sprintf(buf2, "%d", static_cast<int>(270)); } break;
|
case 1: {
|
||||||
|
sprintf(buf2, "%d", static_cast<int>(90));
|
||||||
|
} break;
|
||||||
|
case 2: {
|
||||||
|
sprintf(buf2, "%d", static_cast<int>(180));
|
||||||
|
} break;
|
||||||
|
case 3: {
|
||||||
|
sprintf(buf2, "%d", static_cast<int>(270));
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to specify the layer of 10000 or above to ensure the video is
|
// We need to specify the layer of 10000 or above to ensure the video is
|
||||||
// displayed on top of our SDL display.
|
// displayed on top of our SDL display.
|
||||||
const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd",
|
const char* argv[] = { "", "--layer",
|
||||||
"--aspect-mode", "letterbox", "--vol", "0", "-o", "both",
|
"10010", "--loop",
|
||||||
"--win", buf1, "--orientation", buf2, "", "", "", "", "", "",
|
"--no-osd", "--aspect-mode",
|
||||||
"", "", "", "", "", NULL };
|
"letterbox", "--vol",
|
||||||
|
"0", "-o",
|
||||||
|
"both", "--win",
|
||||||
|
buf1, "--orientation",
|
||||||
|
buf2, "",
|
||||||
|
"", "",
|
||||||
|
"", "",
|
||||||
|
"", "",
|
||||||
|
"", "",
|
||||||
|
"", "",
|
||||||
|
NULL };
|
||||||
|
|
||||||
// Check if we want to mute the audio.
|
// Check if we want to mute the audio.
|
||||||
if ((!Settings::getInstance()->getBool("GamelistVideoAudio") ||
|
if ((!Settings::getInstance()->getBool("GamelistVideoAudio") ||
|
||||||
static_cast<float>(VolumeControl::getInstance()->getVolume()) == 0) ||
|
static_cast<float>(VolumeControl::getInstance()->getVolume()) == 0) ||
|
||||||
(!Settings::getInstance()->getBool("ScreensaverVideoAudio") &&
|
(!Settings::getInstance()->getBool("ScreensaverVideoAudio") &&
|
||||||
mScreensaverMode)) {
|
mScreensaverMode)) {
|
||||||
argv[8] = "-1000000";
|
argv[8] = "-1000000";
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
float percentVolume =
|
float percentVolume =
|
||||||
static_cast<float>(VolumeControl::getInstance()->getVolume());
|
static_cast<float>(VolumeControl::getInstance()->getVolume());
|
||||||
int OMXVolume = static_cast<int>((percentVolume - 98) * 105);
|
int OMXVolume = static_cast<int>((percentVolume - 98) * 105);
|
||||||
argv[8] = std::to_string(OMXVolume).c_str();
|
argv[8] = std::to_string(OMXVolume).c_str();
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@
|
||||||
|
|
||||||
#include "components/VideoVlcComponent.h"
|
#include "components/VideoVlcComponent.h"
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "resources/TextureResource.h"
|
|
||||||
#include "utils/StringUtil.h"
|
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
#include "resources/TextureResource.h"
|
||||||
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
|
@ -26,19 +26,18 @@
|
||||||
#include <vlc/vlc.h>
|
#include <vlc/vlc.h>
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
#include <cstring>
|
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
|
#include <cstring>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
|
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
|
||||||
|
|
||||||
VideoVlcComponent::VideoVlcComponent(
|
VideoVlcComponent::VideoVlcComponent(Window* window)
|
||||||
Window* window)
|
: VideoComponent(window)
|
||||||
: VideoComponent(window),
|
, mMediaPlayer(nullptr)
|
||||||
mMediaPlayer(nullptr),
|
, mMedia(nullptr)
|
||||||
mMedia(nullptr),
|
, mContext({})
|
||||||
mContext({}),
|
, mHasSetAudioVolume(false)
|
||||||
mHasSetAudioVolume(false)
|
|
||||||
{
|
{
|
||||||
// Get an empty texture for rendering the video.
|
// Get an empty texture for rendering the video.
|
||||||
mTexture = TextureResource::get("");
|
mTexture = TextureResource::get("");
|
||||||
|
@ -86,7 +85,7 @@ void VideoVlcComponent::setupVLC()
|
||||||
if (!mVLC) {
|
if (!mVLC) {
|
||||||
const char* args[] = { "--quiet" };
|
const char* args[] = { "--quiet" };
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// It's required to set the VLC_PLUGIN_PATH variable on macOS, or the libVLC
|
// It's required to set the VLC_PLUGIN_PATH variable on macOS, or the libVLC
|
||||||
// initialization will fail (with no error message).
|
// initialization will fail (with no error message).
|
||||||
std::string vlcPluginPath = Utils::FileSystem::getExePath() + "/plugins";
|
std::string vlcPluginPath = Utils::FileSystem::getExePath() + "/plugins";
|
||||||
|
@ -94,7 +93,7 @@ void VideoVlcComponent::setupVLC()
|
||||||
setenv("VLC_PLUGIN_PATH", vlcPluginPath.c_str(), 1);
|
setenv("VLC_PLUGIN_PATH", vlcPluginPath.c_str(), 1);
|
||||||
else
|
else
|
||||||
setenv("VLC_PLUGIN_PATH", "/Applications/VLC.app/Contents/MacOS/plugins/", 1);
|
setenv("VLC_PLUGIN_PATH", "/Applications/VLC.app/Contents/MacOS/plugins/", 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mVLC = libvlc_new(1, args);
|
mVLC = libvlc_new(1, args);
|
||||||
}
|
}
|
||||||
|
@ -105,7 +104,8 @@ void VideoVlcComponent::setupContext()
|
||||||
if (!mContext.valid) {
|
if (!mContext.valid) {
|
||||||
// Create an RGBA surface to render the video into.
|
// Create an RGBA surface to render the video into.
|
||||||
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, static_cast<int>(mVideoWidth),
|
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, static_cast<int>(mVideoWidth),
|
||||||
static_cast<int>(mVideoHeight), 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
|
static_cast<int>(mVideoHeight), 32, 0xff000000,
|
||||||
|
0x00ff0000, 0x0000ff00, 0x000000ff);
|
||||||
mContext.mutex = SDL_CreateMutex();
|
mContext.mutex = SDL_CreateMutex();
|
||||||
mContext.valid = true;
|
mContext.valid = true;
|
||||||
resize();
|
resize();
|
||||||
|
@ -148,7 +148,6 @@ void VideoVlcComponent::resize()
|
||||||
|
|
||||||
mSize[1] = std::round(mSize[1]);
|
mSize[1] = std::round(mSize[1]);
|
||||||
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
||||||
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If both components are set, we just stretch.
|
// If both components are set, we just stretch.
|
||||||
|
@ -196,8 +195,8 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
if (mFadeIn < 1) {
|
if (mFadeIn < 1) {
|
||||||
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
|
const unsigned int fadeIn = static_cast<int>(mFadeIn * 255.0f);
|
||||||
color = Renderer::convertRGBAToABGR((fadeIn << 24) |
|
color =
|
||||||
(fadeIn << 16) | (fadeIn << 8) | 255);
|
Renderer::convertRGBAToABGR((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
color = 0xFFFFFFFF;
|
color = 0xFFFFFFFF;
|
||||||
|
@ -209,13 +208,16 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
||||||
// Render the black rectangle behind the video.
|
// Render the black rectangle behind the video.
|
||||||
if (mVideoRectangleCoords.size() == 4) {
|
if (mVideoRectangleCoords.size() == 4) {
|
||||||
Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
|
Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
|
||||||
mVideoRectangleCoords[2], mVideoRectangleCoords[3], 0x000000FF, 0x000000FF);
|
mVideoRectangleCoords[2], mVideoRectangleCoords[3], // Line break.
|
||||||
|
0x000000FF, 0x000000FF);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
|
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
|
||||||
vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
|
vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
|
||||||
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
|
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
|
||||||
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
|
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Round vertices.
|
// Round vertices.
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
|
@ -223,17 +225,18 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
||||||
|
|
||||||
// Build a texture for the video frame.
|
// Build a texture for the video frame.
|
||||||
mTexture->initFromPixels(reinterpret_cast<unsigned char*>(mContext.surface->pixels),
|
mTexture->initFromPixels(reinterpret_cast<unsigned char*>(mContext.surface->pixels),
|
||||||
mContext.surface->w, mContext.surface->h);
|
mContext.surface->w, mContext.surface->h);
|
||||||
mTexture->bind();
|
mTexture->bind();
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
// Render scanlines if this option is enabled. However, if this is the media viewer
|
// Render scanlines if this option is enabled. However, if this is the media viewer
|
||||||
// or the video screensaver, then skip this as the scanline rendering is then handled
|
// or the video screensaver, then skip this as the scanline rendering is then handled
|
||||||
// in those modules as a postprocessing step.
|
// in those modules as a postprocessing step.
|
||||||
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
||||||
Settings::getInstance()->getBool("GamelistVideoScanlines"))
|
Settings::getInstance()->getBool("GamelistVideoScanlines")) {
|
||||||
vertices[0].shaders = Renderer::SHADER_SCANLINES;
|
vertices[0].shaders = Renderer::SHADER_SCANLINES;
|
||||||
#endif
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Render it.
|
// Render it.
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
@ -283,10 +286,10 @@ void VideoVlcComponent::calculateBlackRectangle()
|
||||||
rectHeight = mSize.y();
|
rectHeight = mSize.y();
|
||||||
}
|
}
|
||||||
// Populate the rectangle coordinates to be used in render().
|
// Populate the rectangle coordinates to be used in render().
|
||||||
mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.x() -
|
mVideoRectangleCoords.push_back(
|
||||||
rectWidth * mOrigin.x()));
|
std::round(mVideoAreaPos.x() - rectWidth * mOrigin.x()));
|
||||||
mVideoRectangleCoords.push_back(std::round(mVideoAreaPos.y() -
|
mVideoRectangleCoords.push_back(
|
||||||
rectHeight * mOrigin.y()));
|
std::round(mVideoAreaPos.y() - rectHeight * mOrigin.y()));
|
||||||
mVideoRectangleCoords.push_back(std::round(rectWidth));
|
mVideoRectangleCoords.push_back(std::round(rectWidth));
|
||||||
mVideoRectangleCoords.push_back(std::round(rectHeight));
|
mVideoRectangleCoords.push_back(std::round(rectHeight));
|
||||||
}
|
}
|
||||||
|
@ -310,20 +313,18 @@ void VideoVlcComponent::setAudioVolume()
|
||||||
bool outputSound = false;
|
bool outputSound = false;
|
||||||
|
|
||||||
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
||||||
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mScreensaverMode && Settings::getInstance()->
|
else if (mScreensaverMode && Settings::getInstance()->getBool("ScreensaverVideoAudio"))
|
||||||
getBool("ScreensaverVideoAudio"))
|
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mMediaViewerMode && Settings::getInstance()->
|
else if (mMediaViewerMode && Settings::getInstance()->getBool("MediaViewerVideoAudio"))
|
||||||
getBool("MediaViewerVideoAudio"))
|
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
|
|
||||||
if (outputSound) {
|
if (outputSound) {
|
||||||
if (libvlc_audio_get_mute(mMediaPlayer) == 1)
|
if (libvlc_audio_get_mute(mMediaPlayer) == 1)
|
||||||
libvlc_audio_set_mute(mMediaPlayer, 0);
|
libvlc_audio_set_mute(mMediaPlayer, 0);
|
||||||
libvlc_audio_set_volume(mMediaPlayer,
|
libvlc_audio_set_volume(mMediaPlayer,
|
||||||
Settings::getInstance()->getInt("SoundVolumeVideos"));
|
Settings::getInstance()->getInt("SoundVolumeVideos"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
libvlc_audio_set_volume(mMediaPlayer, 0);
|
libvlc_audio_set_volume(mMediaPlayer, 0);
|
||||||
|
@ -339,11 +340,11 @@ void VideoVlcComponent::startVideo()
|
||||||
mVideoWidth = 0;
|
mVideoWidth = 0;
|
||||||
mVideoHeight = 0;
|
mVideoHeight = 0;
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
std::string path(Utils::String::replace(mVideoPath, "/", "\\"));
|
std::string path(Utils::String::replace(mVideoPath, "/", "\\"));
|
||||||
#else
|
#else
|
||||||
std::string path(mVideoPath);
|
std::string path(mVideoPath);
|
||||||
#endif
|
#endif
|
||||||
// Make sure we have a video path.
|
// Make sure we have a video path.
|
||||||
if (mVLC && (path.size() > 0)) {
|
if (mVLC && (path.size() > 0)) {
|
||||||
// Set the video that we are going to be playing so we don't attempt to restart it.
|
// Set the video that we are going to be playing so we don't attempt to restart it.
|
||||||
|
@ -356,8 +357,8 @@ void VideoVlcComponent::startVideo()
|
||||||
int parseResult;
|
int parseResult;
|
||||||
|
|
||||||
// Asynchronous media parsing.
|
// Asynchronous media parsing.
|
||||||
libvlc_event_attach(libvlc_media_event_manager(mMedia),
|
libvlc_event_attach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged,
|
||||||
libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
|
VlcMediaParseCallback, 0);
|
||||||
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
|
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
|
||||||
|
|
||||||
if (!parseResult) {
|
if (!parseResult) {
|
||||||
|
@ -382,8 +383,8 @@ void VideoVlcComponent::startVideo()
|
||||||
}
|
}
|
||||||
libvlc_media_tracks_release(tracks, track_count);
|
libvlc_media_tracks_release(tracks, track_count);
|
||||||
libvlc_media_parse_stop(mMedia);
|
libvlc_media_parse_stop(mMedia);
|
||||||
libvlc_event_detach(libvlc_media_event_manager(mMedia),
|
libvlc_event_detach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged,
|
||||||
libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
|
VlcMediaParseCallback, 0);
|
||||||
|
|
||||||
// Make sure we found a valid video track.
|
// Make sure we found a valid video track.
|
||||||
if ((mVideoWidth > 0) && (mVideoHeight > 0)) {
|
if ((mVideoWidth > 0) && (mVideoHeight > 0)) {
|
||||||
|
@ -395,32 +396,36 @@ void VideoVlcComponent::startVideo()
|
||||||
// The code below enables the libVLC audio output to be processed inside ES-DE.
|
// The code below enables the libVLC audio output to be processed inside ES-DE.
|
||||||
// Unfortunately this causes excessive stuttering for some reason that I still
|
// Unfortunately this causes excessive stuttering for some reason that I still
|
||||||
// don't understand, so at the moment this code is disabled.
|
// don't understand, so at the moment this code is disabled.
|
||||||
// auto audioFormatCallback = [](void **data, char *format,
|
// auto audioFormatCallback = [](void **data, char *format,
|
||||||
// unsigned *rate, unsigned *channels) -> int {
|
// unsigned *rate, unsigned *channels) -> int {
|
||||||
// format = const_cast<char*>("F32L");
|
// format = const_cast<char*>("F32L");
|
||||||
// *rate = 48000;
|
// *rate = 48000;
|
||||||
// *channels = 2;
|
// *channels = 2;
|
||||||
// return 0;
|
// return 0;
|
||||||
// };
|
// };
|
||||||
//
|
//
|
||||||
// libvlc_audio_set_format_callbacks(mMediaPlayer,
|
// libvlc_audio_set_format_callbacks(mMediaPlayer,
|
||||||
// audioFormatCallback, nullptr);
|
// audioFormatCallback, nullptr);
|
||||||
//
|
//
|
||||||
// auto audioPlayCallback = [](void* data, const void* samples,
|
// auto audioPlayCallback = [](void* data, const void*
|
||||||
// unsigned count, int64_t pts) {
|
// samples,
|
||||||
// AudioManager::getInstance()->processStream(samples, count);
|
// unsigned count, int64_t pts) {
|
||||||
// };
|
// AudioManager::getInstance()->processStream(samples,
|
||||||
//
|
// count);
|
||||||
// libvlc_audio_set_callbacks(mMediaPlayer, audioPlayCallback,
|
// };
|
||||||
// nullptr, nullptr, nullptr, nullptr, this);
|
//
|
||||||
|
// libvlc_audio_set_callbacks(mMediaPlayer,
|
||||||
|
// audioPlayCallback,
|
||||||
|
// nullptr, nullptr, nullptr, nullptr, this);
|
||||||
|
|
||||||
libvlc_video_set_format(mMediaPlayer, "RGBA", static_cast<int>(mVideoWidth),
|
libvlc_video_set_format(mMediaPlayer, "RGBA", static_cast<int>(mVideoWidth),
|
||||||
static_cast<int>(mVideoHeight), static_cast<int>(mVideoWidth * 4));
|
static_cast<int>(mVideoHeight),
|
||||||
|
static_cast<int>(mVideoWidth * 4));
|
||||||
|
|
||||||
// Lock video memory as a preparation for rendering a frame.
|
// Lock video memory as a preparation for rendering a frame.
|
||||||
auto videoLockCallback = [](void* data, void** p_pixels) -> void* {
|
auto videoLockCallback = [](void* data, void** p_pixels) -> void* {
|
||||||
struct VideoContext* videoContext =
|
struct VideoContext* videoContext =
|
||||||
reinterpret_cast<struct VideoContext*>(data);
|
reinterpret_cast<struct VideoContext*>(data);
|
||||||
SDL_LockMutex(videoContext->mutex);
|
SDL_LockMutex(videoContext->mutex);
|
||||||
SDL_LockSurface(videoContext->surface);
|
SDL_LockSurface(videoContext->surface);
|
||||||
*p_pixels = videoContext->surface->pixels;
|
*p_pixels = videoContext->surface->pixels;
|
||||||
|
@ -428,15 +433,15 @@ void VideoVlcComponent::startVideo()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Unlock the video memory after rendering a frame.
|
// Unlock the video memory after rendering a frame.
|
||||||
auto videoUnlockCallback = [](void* data, void*, void *const*) {
|
auto videoUnlockCallback = [](void* data, void*, void* const*) {
|
||||||
struct VideoContext* videoContext =
|
struct VideoContext* videoContext =
|
||||||
reinterpret_cast<struct VideoContext*>(data);
|
reinterpret_cast<struct VideoContext*>(data);
|
||||||
SDL_UnlockSurface(videoContext->surface);
|
SDL_UnlockSurface(videoContext->surface);
|
||||||
SDL_UnlockMutex(videoContext->mutex);
|
SDL_UnlockMutex(videoContext->mutex);
|
||||||
};
|
};
|
||||||
|
|
||||||
libvlc_video_set_callbacks(mMediaPlayer, videoLockCallback,
|
libvlc_video_set_callbacks(mMediaPlayer, videoLockCallback, videoUnlockCallback,
|
||||||
videoUnlockCallback, nullptr, reinterpret_cast<void*>(&mContext));
|
nullptr, reinterpret_cast<void*>(&mContext));
|
||||||
|
|
||||||
libvlc_media_player_play(mMediaPlayer);
|
libvlc_media_player_play(mMediaPlayer);
|
||||||
|
|
||||||
|
@ -517,7 +522,7 @@ void VideoVlcComponent::handleLooping()
|
||||||
// If the screensaver video swap time is set to 0, it means we should
|
// If the screensaver video swap time is set to 0, it means we should
|
||||||
// skip to the next game when the video has finished playing.
|
// skip to the next game when the video has finished playing.
|
||||||
if (mScreensaverMode &&
|
if (mScreensaverMode &&
|
||||||
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) {
|
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) {
|
||||||
mWindow->screensaverTriggerNextGame();
|
mWindow->screensaverTriggerNextGame();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -527,13 +532,13 @@ void VideoVlcComponent::handleLooping()
|
||||||
bool outputSound = false;
|
bool outputSound = false;
|
||||||
|
|
||||||
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
if ((!mScreensaverMode && !mMediaViewerMode) &&
|
||||||
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
Settings::getInstance()->getBool("GamelistVideoAudio"))
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mScreensaverMode && Settings::getInstance()->
|
else if (mScreensaverMode &&
|
||||||
getBool("ScreensaverVideoAudio"))
|
Settings::getInstance()->getBool("ScreensaverVideoAudio"))
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
else if (mMediaViewerMode && Settings::getInstance()->
|
else if (mMediaViewerMode &&
|
||||||
getBool("MediaViewerVideoAudio"))
|
Settings::getInstance()->getBool("MediaViewerVideoAudio"))
|
||||||
outputSound = true;
|
outputSound = true;
|
||||||
|
|
||||||
if (!outputSound)
|
if (!outputSound)
|
||||||
|
|
|
@ -66,7 +66,7 @@ private:
|
||||||
// Handle looping the video. Must be called periodically.
|
// Handle looping the video. Must be called periodically.
|
||||||
virtual void handleLooping() override;
|
virtual void handleLooping() override;
|
||||||
|
|
||||||
static void VlcMediaParseCallback(const libvlc_event_t *event, void *user_data) {};
|
static void VlcMediaParseCallback(const libvlc_event_t* event, void* user_data) {}
|
||||||
|
|
||||||
static VideoVlcComponent* sInstance;
|
static VideoVlcComponent* sInstance;
|
||||||
static libvlc_instance_t* mVLC;
|
static libvlc_instance_t* mVLC;
|
||||||
|
|
|
@ -10,68 +10,68 @@
|
||||||
|
|
||||||
#include "guis/GuiComplexTextEditPopup.h"
|
#include "guis/GuiComplexTextEditPopup.h"
|
||||||
|
|
||||||
|
#include "Window.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"
|
||||||
#include "guis/GuiMsgBox.h"
|
#include "guis/GuiMsgBox.h"
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
GuiComplexTextEditPopup::GuiComplexTextEditPopup(
|
GuiComplexTextEditPopup::GuiComplexTextEditPopup(
|
||||||
Window* window,
|
Window* window,
|
||||||
const HelpStyle& helpstyle,
|
const HelpStyle& helpstyle,
|
||||||
const std::string& title,
|
const std::string& title,
|
||||||
const std::string& infoString1,
|
const std::string& infoString1,
|
||||||
const std::string& infoString2,
|
const std::string& infoString2,
|
||||||
const std::string& initValue,
|
const std::string& initValue,
|
||||||
const std::function<void(const std::string&)>& okCallback,
|
const std::function<void(const std::string&)>& okCallback,
|
||||||
bool multiLine,
|
bool multiLine,
|
||||||
const std::string& acceptBtnText,
|
const std::string& acceptBtnText,
|
||||||
const std::string& saveConfirmationText,
|
const std::string& saveConfirmationText,
|
||||||
const std::string& loadBtnText,
|
const std::string& loadBtnText,
|
||||||
const std::string& loadBtnHelpText,
|
const std::string& loadBtnHelpText,
|
||||||
const std::string& clearBtnText,
|
const std::string& clearBtnText,
|
||||||
const std::string& clearBtnHelpText,
|
const std::string& clearBtnHelpText,
|
||||||
bool hideCancelButton)
|
bool hideCancelButton)
|
||||||
: GuiComponent(window),
|
: GuiComponent(window)
|
||||||
mHelpStyle(helpstyle),
|
, mHelpStyle(helpstyle)
|
||||||
mBackground(window, ":/graphics/frame.svg"),
|
, mBackground(window, ":/graphics/frame.svg")
|
||||||
mGrid(window, Vector2i(1, 5)),
|
, mGrid(window, Vector2i(1, 5))
|
||||||
mMultiLine(multiLine),
|
, mMultiLine(multiLine)
|
||||||
mInitValue(initValue),
|
, mInitValue(initValue)
|
||||||
mOkCallback(okCallback),
|
, mOkCallback(okCallback)
|
||||||
mSaveConfirmationText(saveConfirmationText),
|
, mSaveConfirmationText(saveConfirmationText)
|
||||||
mHideCancelButton(hideCancelButton)
|
, mHideCancelButton(hideCancelButton)
|
||||||
{
|
{
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
|
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
|
||||||
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
||||||
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1,
|
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1, Font::get(FONT_SIZE_SMALL),
|
||||||
Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
0x555555FF, ALIGN_CENTER);
|
||||||
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2,
|
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2, Font::get(FONT_SIZE_SMALL),
|
||||||
Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
|
0x555555FF, ALIGN_CENTER);
|
||||||
|
|
||||||
mText = std::make_shared<TextEditComponent>(mWindow);
|
mText = std::make_shared<TextEditComponent>(mWindow);
|
||||||
mText->setValue(initValue);
|
mText->setValue(initValue);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
|
||||||
[this, okCallback] {
|
[this, okCallback] {
|
||||||
okCallback(mText->getValue());
|
okCallback(mText->getValue());
|
||||||
delete this;
|
delete this;
|
||||||
}));
|
}));
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, loadBtnText, loadBtnHelpText,
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, loadBtnText, loadBtnHelpText,
|
||||||
[this, infoString2] {
|
[this, infoString2] {
|
||||||
mText->setValue(infoString2);
|
mText->setValue(infoString2);
|
||||||
mText->setCursor(0);
|
mText->setCursor(0);
|
||||||
mText->setCursor(infoString2.size());
|
mText->setCursor(infoString2.size());
|
||||||
}));
|
}));
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, clearBtnText, clearBtnHelpText,
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, clearBtnText, clearBtnHelpText,
|
||||||
[this] { mText->setValue(""); }));
|
[this] { mText->setValue(""); }));
|
||||||
if (!mHideCancelButton)
|
if (!mHideCancelButton)
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
|
||||||
[this] { delete this; }));
|
[this] { delete this; }));
|
||||||
|
|
||||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||||
|
|
||||||
|
@ -79,14 +79,14 @@ GuiComplexTextEditPopup::GuiComplexTextEditPopup(
|
||||||
mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true);
|
mGrid.setEntry(mInfoString1, Vector2i(0, 1), false, true);
|
||||||
mGrid.setEntry(mInfoString2, Vector2i(0, 2), false, false);
|
mGrid.setEntry(mInfoString2, Vector2i(0, 2), false, false);
|
||||||
mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1),
|
mGrid.setEntry(mText, Vector2i(0, 3), true, false, Vector2i(1, 1),
|
||||||
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
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.15f, true);
|
mGrid.setRowHeightPerc(1, 0.15f, true);
|
||||||
|
|
||||||
float textHeight = mText->getFont()->getHeight();
|
float textHeight = mText->getFont()->getHeight();
|
||||||
|
|
||||||
if (multiLine)
|
if (multiLine)
|
||||||
textHeight *= 6;
|
textHeight *= 6.0f;
|
||||||
|
|
||||||
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
||||||
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
||||||
|
@ -97,18 +97,17 @@ GuiComplexTextEditPopup::GuiComplexTextEditPopup(
|
||||||
mText->setSize(0, textHeight);
|
mText->setSize(0, textHeight);
|
||||||
mInfoString2->setSize(infoWidth, mInfoString2->getFont()->getHeight());
|
mInfoString2->setSize(infoWidth, mInfoString2->getFont()->getHeight());
|
||||||
|
|
||||||
setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight +
|
setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() +
|
||||||
mButtonGrid->getSize().y() + mButtonGrid->getSize().y() * 1.85f);
|
mButtonGrid->getSize().y() * 1.85f);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||||
(Renderer::getScreenHeight() - mSize.y()) / 2);
|
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||||
mText->startEditing();
|
mText->startEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiComplexTextEditPopup::onSizeChanged()
|
void GuiComplexTextEditPopup::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
mText->setSize(mSize.x() - 40.0f, 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());
|
||||||
|
@ -122,13 +121,22 @@ bool GuiComplexTextEditPopup::input(InputConfig* config, Input input)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!mHideCancelButton) {
|
if (!mHideCancelButton) {
|
||||||
// Pressing back when not text editing closes us.
|
// Pressing back when not text editing closes us.
|
||||||
if (config->isMappedTo("b", input) && input.value) {
|
if (config->isMappedTo("b", input) && input.value) {
|
||||||
if (mText->getValue() != mInitValue) {
|
if (mText->getValue() != mInitValue) {
|
||||||
// Changes were made, ask if the user wants to save them.
|
// Changes were made, ask if the user wants to save them.
|
||||||
mWindow->pushGui(new GuiMsgBox(mWindow, mHelpStyle, mSaveConfirmationText, "YES",
|
mWindow->pushGui(new GuiMsgBox(
|
||||||
[this] { this->mOkCallback(mText->getValue()); delete this; return true; },
|
mWindow, mHelpStyle, mSaveConfirmationText, "YES",
|
||||||
"NO", [this] { delete this; return false; }));
|
[this] {
|
||||||
|
this->mOkCallback(mText->getValue());
|
||||||
|
delete this;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"NO",
|
||||||
|
[this] {
|
||||||
|
delete this;
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete this;
|
delete this;
|
||||||
|
|
|
@ -11,9 +11,9 @@
|
||||||
#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
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
class TextEditComponent;
|
class TextEditComponent;
|
||||||
|
@ -21,28 +21,27 @@ class TextEditComponent;
|
||||||
class GuiComplexTextEditPopup : public GuiComponent
|
class GuiComplexTextEditPopup : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiComplexTextEditPopup(
|
GuiComplexTextEditPopup(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
const std::string& title,
|
||||||
const std::string& title,
|
const std::string& infoString1,
|
||||||
const std::string& infoString1,
|
const std::string& infoString2,
|
||||||
const std::string& infoString2,
|
const std::string& initValue,
|
||||||
const std::string& initValue,
|
const std::function<void(const std::string&)>& okCallback,
|
||||||
const std::function<void(const std::string&)>& okCallback,
|
bool multiLine,
|
||||||
bool multiLine,
|
const std::string& acceptBtnText = "OK",
|
||||||
const std::string& acceptBtnText = "OK",
|
const std::string& saveConfirmationText = "SAVE CHANGES?",
|
||||||
const std::string& saveConfirmationText = "SAVE CHANGES?",
|
const std::string& loadBtnText = "LOAD",
|
||||||
const std::string& loadBtnText = "LOAD",
|
const std::string& loadBtnHelpText = "load default",
|
||||||
const std::string& loadBtnHelpText = "load default",
|
const std::string& clearBtnText = "CLEAR",
|
||||||
const std::string& clearBtnText = "CLEAR",
|
const std::string& clearBtnHelpText = "clear",
|
||||||
const std::string& clearBtnHelpText = "clear",
|
bool hideCancelButton = false);
|
||||||
bool hideCancelButton = false);
|
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
|
|
|
@ -8,25 +8,24 @@
|
||||||
|
|
||||||
#include "guis/GuiDetectDevice.h"
|
#include "guis/GuiDetectDevice.h"
|
||||||
|
|
||||||
|
#include "InputManager.h"
|
||||||
|
#include "Window.h"
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
#include "guis/GuiInputConfig.h"
|
#include "guis/GuiInputConfig.h"
|
||||||
#include "utils/FileSystemUtil.h"
|
#include "utils/FileSystemUtil.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "InputManager.h"
|
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
#define HOLD_TIME 1000
|
#define HOLD_TIME 1000
|
||||||
|
|
||||||
GuiDetectDevice::GuiDetectDevice(
|
GuiDetectDevice::GuiDetectDevice(Window* window,
|
||||||
Window* window,
|
bool firstRun,
|
||||||
bool firstRun,
|
bool forcedConfig,
|
||||||
bool forcedConfig,
|
const std::function<void()>& doneCallback)
|
||||||
const std::function<void()>& doneCallback)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mFirstRun(firstRun)
|
||||||
mFirstRun(firstRun),
|
, mForcedConfig(forcedConfig)
|
||||||
mForcedConfig(forcedConfig),
|
, mBackground(window, ":/graphics/frame.svg")
|
||||||
mBackground(window, ":/graphics/frame.svg"),
|
, mGrid(window, Vector2i(1, 5))
|
||||||
mGrid(window, Vector2i(1, 5))
|
|
||||||
{
|
{
|
||||||
mHoldingConfig = nullptr;
|
mHoldingConfig = nullptr;
|
||||||
mHoldTime = 0;
|
mHoldTime = 0;
|
||||||
|
@ -36,8 +35,9 @@ GuiDetectDevice::GuiDetectDevice(
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
// Title.
|
// Title.
|
||||||
mTitle = std::make_shared<TextComponent>(mWindow, firstRun ? "WELCOME" :
|
mTitle =
|
||||||
"CONFIGURE INPUT DEVICE", Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
std::make_shared<TextComponent>(mWindow, firstRun ? "WELCOME" : "CONFIGURE INPUT DEVICE",
|
||||||
|
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM);
|
mGrid.setEntry(mTitle, Vector2i(0, 0), false, true, Vector2i(1, 1), GridFlags::BORDER_BOTTOM);
|
||||||
|
|
||||||
// Device info.
|
// Device info.
|
||||||
|
@ -52,33 +52,33 @@ GuiDetectDevice::GuiDetectDevice(
|
||||||
if (numDevices > 1 && Settings::getInstance()->getBool("InputOnlyFirstController"))
|
if (numDevices > 1 && Settings::getInstance()->getBool("InputOnlyFirstController"))
|
||||||
deviceInfo << " (ONLY ACCEPTING INPUT FROM FIRST CONTROLLER)";
|
deviceInfo << " (ONLY ACCEPTING INPUT FROM FIRST CONTROLLER)";
|
||||||
|
|
||||||
mDeviceInfo = std::make_shared<TextComponent>(mWindow, deviceInfo.str(),
|
mDeviceInfo = std::make_shared<TextComponent>(
|
||||||
Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
mWindow, deviceInfo.str(), Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true);
|
mGrid.setEntry(mDeviceInfo, Vector2i(0, 1), false, true);
|
||||||
|
|
||||||
// Message.
|
// Message.
|
||||||
if (numDevices > 0) {
|
if (numDevices > 0) {
|
||||||
mMsg1 = std::make_shared<TextComponent>(mWindow,
|
mMsg1 = std::make_shared<TextComponent>(
|
||||||
"HOLD A BUTTON ON YOUR GAMEPAD OR KEYBOARD TO CONFIGURE IT",
|
mWindow, "HOLD A BUTTON ON YOUR GAMEPAD OR KEYBOARD TO CONFIGURE IT",
|
||||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mMsg1 = std::make_shared<TextComponent>(mWindow,
|
mMsg1 = std::make_shared<TextComponent>(
|
||||||
"HOLD A BUTTON ON YOUR KEYBOARD TO CONFIGURE IT",
|
mWindow, "HOLD A BUTTON ON YOUR KEYBOARD TO CONFIGURE IT", Font::get(FONT_SIZE_SMALL),
|
||||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
0x777777FF, ALIGN_CENTER);
|
||||||
}
|
}
|
||||||
|
|
||||||
mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true);
|
mGrid.setEntry(mMsg1, Vector2i(0, 2), false, true);
|
||||||
|
|
||||||
const std::string msg2str = firstRun ?
|
const std::string msg2str =
|
||||||
"PRESS ESC TO SKIP (OR F4 TO QUIT AT ANY TIME)" : "PRESS ESC TO CANCEL";
|
firstRun ? "PRESS ESC TO SKIP (OR F4 TO QUIT AT ANY TIME)" : "PRESS ESC TO CANCEL";
|
||||||
mMsg2 = std::make_shared<TextComponent>(mWindow, msg2str,
|
mMsg2 = std::make_shared<TextComponent>(mWindow, msg2str, Font::get(FONT_SIZE_SMALL),
|
||||||
Font::get(FONT_SIZE_SMALL), 0x777777FF, ALIGN_CENTER);
|
0x777777FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mMsg2, Vector2i(0, 3), false, true);
|
mGrid.setEntry(mMsg2, Vector2i(0, 3), false, true);
|
||||||
|
|
||||||
// Currently held device.
|
// Currently held device.
|
||||||
mDeviceHeld = std::make_shared<TextComponent>(mWindow, "",
|
mDeviceHeld = std::make_shared<TextComponent>(mWindow, "", Font::get(FONT_SIZE_MEDIUM),
|
||||||
Font::get(FONT_SIZE_MEDIUM), 0xFFFFFFFF, ALIGN_CENTER);
|
0xFFFFFFFF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true);
|
mGrid.setEntry(mDeviceHeld, Vector2i(0, 4), false, true);
|
||||||
|
|
||||||
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
||||||
|
@ -87,27 +87,27 @@ GuiDetectDevice::GuiDetectDevice(
|
||||||
float width = Math::clamp(0.60f * aspectValue, 0.50f, 0.80f) * Renderer::getScreenWidth();
|
float width = Math::clamp(0.60f * aspectValue, 0.50f, 0.80f) * Renderer::getScreenWidth();
|
||||||
|
|
||||||
setSize(width, Renderer::getScreenHeight() * 0.5f);
|
setSize(width, Renderer::getScreenHeight() * 0.5f);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2,
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||||
(Renderer::getScreenHeight() - mSize.y()) / 2);
|
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiDetectDevice::onSizeChanged()
|
void GuiDetectDevice::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
|
||||||
// Grid.
|
// Grid.
|
||||||
mGrid.setSize(mSize);
|
mGrid.setSize(mSize);
|
||||||
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y());
|
||||||
//mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y());
|
// mGrid.setRowHeightPerc(1, mDeviceInfo->getFont()->getHeight() / mSize.y());
|
||||||
mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(2, mMsg1->getFont()->getHeight() / mSize.y());
|
||||||
mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y());
|
mGrid.setRowHeightPerc(3, mMsg2->getFont()->getHeight() / mSize.y());
|
||||||
//mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y());
|
// mGrid.setRowHeightPerc(4, mDeviceHeld->getFont()->getHeight() / mSize.y());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GuiDetectDevice::input(InputConfig* config, Input input)
|
bool GuiDetectDevice::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY &&
|
if (!mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY && input.value &&
|
||||||
input.value && input.id == SDLK_ESCAPE) {
|
input.id == SDLK_ESCAPE) {
|
||||||
// Cancel the configuration.
|
// Cancel the configuration.
|
||||||
delete this; // Delete GUI element.
|
delete this; // Delete GUI element.
|
||||||
return true;
|
return true;
|
||||||
|
@ -115,15 +115,15 @@ bool GuiDetectDevice::input(InputConfig* config, Input input)
|
||||||
// First run, but the user chooses to skip the configuration. This will default to the
|
// First run, but the user chooses to skip the configuration. This will default to the
|
||||||
// built-in keyboard mappings.
|
// built-in keyboard mappings.
|
||||||
else if (mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY &&
|
else if (mFirstRun && input.device == DEVICE_KEYBOARD && input.type == TYPE_KEY &&
|
||||||
input.value && input.id == SDLK_ESCAPE) {
|
input.value && input.id == SDLK_ESCAPE) {
|
||||||
if (mDoneCallback)
|
if (mDoneCallback)
|
||||||
mDoneCallback();
|
mDoneCallback();
|
||||||
delete this; // Delete GUI element.
|
delete this; // Delete GUI element.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (input.type == TYPE_BUTTON || input.type == TYPE_AXIS || input.type == TYPE_KEY ||
|
if (input.type == TYPE_BUTTON || input.type == TYPE_AXIS || input.type == TYPE_KEY ||
|
||||||
input.type == TYPE_CEC_BUTTON) {
|
input.type == TYPE_CEC_BUTTON) {
|
||||||
if (input.value && mHoldingConfig == nullptr) {
|
if (input.value && mHoldingConfig == nullptr) {
|
||||||
// Started holding.
|
// Started holding.
|
||||||
mHoldingConfig = config;
|
mHoldingConfig = config;
|
||||||
|
@ -146,8 +146,8 @@ void GuiDetectDevice::update(int deltaTime)
|
||||||
// configuration unless the flag to force the configuration was passed on the
|
// configuration unless the flag to force the configuration was passed on the
|
||||||
// command line.
|
// command line.
|
||||||
if (!mForcedConfig && mFirstRun &&
|
if (!mForcedConfig && mFirstRun &&
|
||||||
Utils::FileSystem::exists(InputManager::getConfigPath()) &&
|
Utils::FileSystem::exists(InputManager::getConfigPath()) &&
|
||||||
InputManager::getInstance()->getNumConfiguredDevices() > 0) {
|
InputManager::getInstance()->getNumConfiguredDevices() > 0) {
|
||||||
if (mDoneCallback)
|
if (mDoneCallback)
|
||||||
mDoneCallback();
|
mDoneCallback();
|
||||||
delete this; // Delete GUI element.
|
delete this; // Delete GUI element.
|
||||||
|
|
|
@ -9,17 +9,19 @@
|
||||||
#ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
#ifndef ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
||||||
#define ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
#define ES_CORE_GUIS_GUI_DETECT_DEVICE_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
|
|
||||||
class GuiDetectDevice : public GuiComponent
|
class GuiDetectDevice : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiDetectDevice(Window* window, bool firstRun, bool forcedConfig,
|
GuiDetectDevice(Window* window,
|
||||||
const std::function<void()>& doneCallback);
|
bool firstRun,
|
||||||
|
bool forcedConfig,
|
||||||
|
const std::function<void()>& doneCallback);
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
|
|
|
@ -8,12 +8,12 @@
|
||||||
|
|
||||||
#include "guis/GuiInputConfig.h"
|
#include "guis/GuiInputConfig.h"
|
||||||
|
|
||||||
#include "components/ButtonComponent.h"
|
|
||||||
#include "components/MenuComponent.h"
|
|
||||||
#include "guis/GuiMsgBox.h"
|
|
||||||
#include "InputManager.h"
|
#include "InputManager.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Window.h"
|
#include "Window.h"
|
||||||
|
#include "components/ButtonComponent.h"
|
||||||
|
#include "components/MenuComponent.h"
|
||||||
|
#include "guis/GuiMsgBox.h"
|
||||||
|
|
||||||
#define HOLD_TO_SKIP_MS 1000
|
#define HOLD_TO_SKIP_MS 1000
|
||||||
|
|
||||||
|
@ -27,23 +27,22 @@ struct InputConfigStructure {
|
||||||
static const int inputCount = 24;
|
static const int inputCount = 24;
|
||||||
static InputConfigStructure sGuiInputConfigList[inputCount];
|
static InputConfigStructure sGuiInputConfigList[inputCount];
|
||||||
|
|
||||||
GuiInputConfig::GuiInputConfig(
|
GuiInputConfig::GuiInputConfig(Window* window,
|
||||||
Window* window,
|
InputConfig* target,
|
||||||
InputConfig* target,
|
bool reconfigureAll,
|
||||||
bool reconfigureAll,
|
const std::function<void()>& okCallback)
|
||||||
const std::function<void()>& okCallback)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mBackground(window, ":/graphics/frame.svg")
|
||||||
mBackground(window, ":/graphics/frame.svg"),
|
, mGrid(window, Vector2i(1, 7))
|
||||||
mGrid(window, Vector2i(1, 7)),
|
, mTargetConfig(target)
|
||||||
mTargetConfig(target),
|
, mHoldingInput(false)
|
||||||
mHoldingInput(false)
|
|
||||||
{
|
{
|
||||||
// Populate the configuration list with the text and icons applicable to the
|
// Populate the configuration list with the text and icons applicable to the
|
||||||
// configured controller type.
|
// configured controller type.
|
||||||
populateConfigList();
|
populateConfigList();
|
||||||
|
|
||||||
LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " (" <<
|
LOG(LogInfo) << "Configuring device " << target->getDeviceId() << " ("
|
||||||
target->getDeviceName() << ").";
|
<< target->getDeviceName() << ").";
|
||||||
|
|
||||||
if (reconfigureAll)
|
if (reconfigureAll)
|
||||||
target->clear();
|
target->clear();
|
||||||
|
@ -57,8 +56,8 @@ GuiInputConfig::GuiInputConfig(
|
||||||
// 0 is a spacer row.
|
// 0 is a spacer row.
|
||||||
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false);
|
mGrid.setEntry(std::make_shared<GuiComponent>(mWindow), Vector2i(0, 0), false);
|
||||||
|
|
||||||
mTitle = std::make_shared<TextComponent>(mWindow, "CONFIGURING",
|
mTitle = std::make_shared<TextComponent>(mWindow, "CONFIGURING", Font::get(FONT_SIZE_LARGE),
|
||||||
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
|
0x555555FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mTitle, Vector2i(0, 1), false, true);
|
mGrid.setEntry(mTitle, Vector2i(0, 1), false, true);
|
||||||
|
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
@ -67,13 +66,15 @@ GuiInputConfig::GuiInputConfig(
|
||||||
else if (target->getDeviceId() == DEVICE_CEC)
|
else if (target->getDeviceId() == DEVICE_CEC)
|
||||||
ss << "CEC";
|
ss << "CEC";
|
||||||
else
|
else
|
||||||
ss << "GAMEPAD " << (target->getDeviceId() + 1) << " (" << target->getDeviceName() << ")";
|
ss << "GAMEPAD " << (target->getDeviceId() + 1) << " (" << target->getDeviceName() << ")";
|
||||||
mSubtitle1 = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(ss.str()),
|
mSubtitle1 =
|
||||||
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(ss.str()),
|
||||||
|
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
||||||
mGrid.setEntry(mSubtitle1, Vector2i(0, 2), false, true);
|
mGrid.setEntry(mSubtitle1, Vector2i(0, 2), false, true);
|
||||||
|
|
||||||
mSubtitle2 = std::make_shared<TextComponent>(mWindow, "HOLD ANY BUTTON 1 SECOND TO SKIP",
|
mSubtitle2 =
|
||||||
Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
std::make_shared<TextComponent>(mWindow, "HOLD ANY BUTTON 1 SECOND TO SKIP",
|
||||||
|
Font::get(FONT_SIZE_SMALL), 0x999999FF, ALIGN_CENTER);
|
||||||
// The opacity will be set to visible for any row that is skippable.
|
// The opacity will be set to visible for any row that is skippable.
|
||||||
mSubtitle2->setOpacity(0);
|
mSubtitle2->setOpacity(0);
|
||||||
|
|
||||||
|
@ -98,12 +99,13 @@ GuiInputConfig::GuiInputConfig(
|
||||||
spacer->setSize(16, 0);
|
spacer->setSize(16, 0);
|
||||||
row.addElement(spacer, false);
|
row.addElement(spacer, false);
|
||||||
|
|
||||||
auto text = std::make_shared<TextComponent>(mWindow,
|
auto text = std::make_shared<TextComponent>(mWindow, sGuiInputConfigList[i].dispName,
|
||||||
sGuiInputConfigList[i].dispName, Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
||||||
row.addElement(text, true);
|
row.addElement(text, true);
|
||||||
|
|
||||||
auto mapping = std::make_shared<TextComponent>(mWindow, "-NOT DEFINED-",
|
auto mapping = std::make_shared<TextComponent>(mWindow, "-NOT DEFINED-",
|
||||||
Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT), 0x999999FF, ALIGN_RIGHT);
|
Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT),
|
||||||
|
0x999999FF, ALIGN_RIGHT);
|
||||||
setNotDefined(mapping); // Overrides the text and color set above.
|
setNotDefined(mapping); // Overrides the text and color set above.
|
||||||
row.addElement(mapping, true);
|
row.addElement(mapping, true);
|
||||||
mMappings.push_back(mapping);
|
mMappings.push_back(mapping);
|
||||||
|
@ -142,9 +144,10 @@ GuiInputConfig::GuiInputConfig(
|
||||||
else {
|
else {
|
||||||
// Button released. Make sure we were holding something and we let go of
|
// Button released. Make sure we were holding something and we let go of
|
||||||
// what we were previously holding.
|
// what we were previously holding.
|
||||||
if (!mHoldingInput || mHeldInput.device != input.device || mHeldInput.id !=
|
if (!mHoldingInput || mHeldInput.device != input.device ||
|
||||||
input.id || mHeldInput.type != input.type)
|
mHeldInput.id != input.id || mHeldInput.type != input.type) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
mHoldingInput = false;
|
mHoldingInput = false;
|
||||||
|
|
||||||
|
@ -177,8 +180,8 @@ GuiInputConfig::GuiInputConfig(
|
||||||
delete this;
|
delete this;
|
||||||
};
|
};
|
||||||
|
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "OK", "ok",
|
||||||
(mWindow, "OK", "ok", [this, okFunction] { okFunction(); }));
|
[this, okFunction] { okFunction(); }));
|
||||||
|
|
||||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||||
mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false);
|
mGrid.setEntry(mButtonGrid, Vector2i(0, 6), true, false);
|
||||||
|
@ -190,122 +193,76 @@ GuiInputConfig::GuiInputConfig(
|
||||||
|
|
||||||
setSize(width, Renderer::getScreenHeight() * 0.75f);
|
setSize(width, Renderer::getScreenHeight() * 0.75f);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::populateConfigList()
|
void GuiInputConfig::populateConfigList()
|
||||||
{
|
{
|
||||||
std::string controllerType = Settings::getInstance()->getString("InputControllerType");
|
std::string controllerType = Settings::getInstance()->getString("InputControllerType");
|
||||||
|
|
||||||
sGuiInputConfigList[0] =
|
// clang-format off
|
||||||
{ "Up", false, "D-PAD UP", ":/help/dpad_up.svg" };
|
sGuiInputConfigList[0] = { "Up", false, "D-PAD UP", ":/help/dpad_up.svg" };
|
||||||
sGuiInputConfigList[1] =
|
sGuiInputConfigList[1] = { "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" };
|
||||||
{ "Down", false, "D-PAD DOWN", ":/help/dpad_down.svg" };
|
sGuiInputConfigList[2] = { "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" };
|
||||||
sGuiInputConfigList[2] =
|
sGuiInputConfigList[3] = { "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" };
|
||||||
{ "Left", false, "D-PAD LEFT", ":/help/dpad_left.svg" };
|
|
||||||
sGuiInputConfigList[3] =
|
|
||||||
{ "Right", false, "D-PAD RIGHT", ":/help/dpad_right.svg" };
|
|
||||||
|
|
||||||
if (controllerType == "snes") {
|
if (controllerType == "snes") {
|
||||||
sGuiInputConfigList[4] =
|
sGuiInputConfigList[4] = { "Back", false, "SELECT", ":/help/button_back_SNES.svg" };
|
||||||
{ "Back", false, "SELECT", ":/help/button_back_SNES.svg" };
|
sGuiInputConfigList[5] = { "Start", false, "START", ":/help/button_start_SNES.svg" };
|
||||||
sGuiInputConfigList[5] =
|
sGuiInputConfigList[6] = { "A", false, "B", ":/help/mbuttons_a_SNES.svg" };
|
||||||
{ "Start", false, "START", ":/help/button_start_SNES.svg" };
|
sGuiInputConfigList[7] = { "B", false, "A", ":/help/mbuttons_b_SNES.svg" };
|
||||||
sGuiInputConfigList[6] =
|
sGuiInputConfigList[8] = { "X", true, "Y", ":/help/mbuttons_x_SNES.svg" };
|
||||||
{ "A", false, "B", ":/help/mbuttons_a_SNES.svg" };
|
sGuiInputConfigList[9] = { "Y", true, "X", ":/help/mbuttons_y_SNES.svg" };
|
||||||
sGuiInputConfigList[7] =
|
|
||||||
{ "B", false, "A", ":/help/mbuttons_b_SNES.svg" };
|
|
||||||
sGuiInputConfigList[8] =
|
|
||||||
{ "X", true, "Y", ":/help/mbuttons_x_SNES.svg" };
|
|
||||||
sGuiInputConfigList[9] =
|
|
||||||
{ "Y", true, "X", ":/help/mbuttons_y_SNES.svg" };
|
|
||||||
}
|
}
|
||||||
else if (controllerType == "ps4") {
|
else if (controllerType == "ps4") {
|
||||||
sGuiInputConfigList[4] =
|
sGuiInputConfigList[4] = { "Back", false, "SHARE", ":/help/button_back_PS4.svg" };
|
||||||
{ "Back", false, "SHARE", ":/help/button_back_PS4.svg" };
|
sGuiInputConfigList[5] = { "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" };
|
||||||
sGuiInputConfigList[5] =
|
sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" };
|
||||||
{ "Start", false, "OPTIONS", ":/help/button_start_PS4.svg" };
|
sGuiInputConfigList[7] = { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" };
|
||||||
sGuiInputConfigList[6] =
|
sGuiInputConfigList[8] = { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" };
|
||||||
{ "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" };
|
sGuiInputConfigList[9] = { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" };
|
||||||
sGuiInputConfigList[7] =
|
|
||||||
{ "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" };
|
|
||||||
sGuiInputConfigList[8] =
|
|
||||||
{ "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" };
|
|
||||||
sGuiInputConfigList[9] =
|
|
||||||
{ "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" };
|
|
||||||
}
|
}
|
||||||
else if (controllerType == "ps5") {
|
else if (controllerType == "ps5") {
|
||||||
sGuiInputConfigList[4] =
|
sGuiInputConfigList[4] = { "Back", false, "CREATE", ":/help/button_back_PS5.svg" };
|
||||||
{ "Back", false, "CREATE", ":/help/button_back_PS5.svg" };
|
sGuiInputConfigList[5] = { "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" };
|
||||||
sGuiInputConfigList[5] =
|
sGuiInputConfigList[6] = { "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" };
|
||||||
{ "Start", false, "OPTIONS", ":/help/button_start_PS5.svg" };
|
sGuiInputConfigList[7] = { "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" };
|
||||||
sGuiInputConfigList[6] =
|
sGuiInputConfigList[8] = { "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" };
|
||||||
{ "A", false, "CROSS", ":/help/mbuttons_a_PS.svg" };
|
sGuiInputConfigList[9] = { "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" };
|
||||||
sGuiInputConfigList[7] =
|
|
||||||
{ "B", false, "CIRCLE", ":/help/mbuttons_b_PS.svg" };
|
|
||||||
sGuiInputConfigList[8] =
|
|
||||||
{ "X", true, "SQUARE", ":/help/mbuttons_x_PS.svg" };
|
|
||||||
sGuiInputConfigList[9] =
|
|
||||||
{ "Y", true, "TRIANGLE", ":/help/mbuttons_y_PS.svg" };
|
|
||||||
}
|
}
|
||||||
else if (controllerType == "xbox360") {
|
else if (controllerType == "xbox360") {
|
||||||
sGuiInputConfigList[4] =
|
sGuiInputConfigList[4] = { "Back", false, "BACK", ":/help/button_back_XBOX360.svg" };
|
||||||
{ "Back", false, "BACK", ":/help/button_back_XBOX360.svg" };
|
sGuiInputConfigList[5] = { "Start", false, "START", ":/help/button_start_XBOX360.svg" };
|
||||||
sGuiInputConfigList[5] =
|
sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" };
|
||||||
{ "Start", false, "START", ":/help/button_start_XBOX360.svg" };
|
sGuiInputConfigList[7] = { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" };
|
||||||
sGuiInputConfigList[6] =
|
sGuiInputConfigList[8] = { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" };
|
||||||
{ "A", false, "A", ":/help/mbuttons_a_XBOX.svg" };
|
sGuiInputConfigList[9] = { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" };
|
||||||
sGuiInputConfigList[7] =
|
|
||||||
{ "B", false, "B", ":/help/mbuttons_b_XBOX.svg" };
|
|
||||||
sGuiInputConfigList[8] =
|
|
||||||
{ "X", true, "X", ":/help/mbuttons_x_XBOX.svg" };
|
|
||||||
sGuiInputConfigList[9] =
|
|
||||||
{ "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" };
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Xbox One and later.
|
// Xbox One and later.
|
||||||
sGuiInputConfigList[4] =
|
sGuiInputConfigList[4] = { "Back", false, "VIEW", ":/help/button_back_XBOX.svg" };
|
||||||
{ "Back", false, "VIEW", ":/help/button_back_XBOX.svg" };
|
sGuiInputConfigList[5] = { "Start", false, "MENU", ":/help/button_start_XBOX.svg" };
|
||||||
sGuiInputConfigList[5] =
|
sGuiInputConfigList[6] = { "A", false, "A", ":/help/mbuttons_a_XBOX.svg" };
|
||||||
{ "Start", false, "MENU", ":/help/button_start_XBOX.svg" };
|
sGuiInputConfigList[7] = { "B", false, "B", ":/help/mbuttons_b_XBOX.svg" };
|
||||||
sGuiInputConfigList[6] =
|
sGuiInputConfigList[8] = { "X", true, "X", ":/help/mbuttons_x_XBOX.svg" };
|
||||||
{ "A", false, "A", ":/help/mbuttons_a_XBOX.svg" };
|
sGuiInputConfigList[9] = { "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" };
|
||||||
sGuiInputConfigList[7] =
|
|
||||||
{ "B", false, "B", ":/help/mbuttons_b_XBOX.svg" };
|
|
||||||
sGuiInputConfigList[8] =
|
|
||||||
{ "X", true, "X", ":/help/mbuttons_x_XBOX.svg" };
|
|
||||||
sGuiInputConfigList[9] =
|
|
||||||
{ "Y", true, "Y", ":/help/mbuttons_y_XBOX.svg" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sGuiInputConfigList[10] =
|
sGuiInputConfigList[10] = { "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" };
|
||||||
{ "LeftShoulder", true, "LEFT SHOULDER", ":/help/button_l.svg" };
|
sGuiInputConfigList[11] = { "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" };
|
||||||
sGuiInputConfigList[11] =
|
sGuiInputConfigList[12] = { "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" };
|
||||||
{ "RightShoulder", true, "RIGHT SHOULDER", ":/help/button_r.svg" };
|
sGuiInputConfigList[13] = { "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" };
|
||||||
sGuiInputConfigList[12] =
|
sGuiInputConfigList[14] = { "LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg" };
|
||||||
{ "LeftTrigger", true, "LEFT TRIGGER", ":/help/button_lt.svg" };
|
sGuiInputConfigList[15] = { "LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" };
|
||||||
sGuiInputConfigList[13] =
|
sGuiInputConfigList[16] = { "LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" };
|
||||||
{ "RightTrigger", true, "RIGHT TRIGGER", ":/help/button_rt.svg" };
|
sGuiInputConfigList[17] = { "LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" };
|
||||||
sGuiInputConfigList[14] =
|
sGuiInputConfigList[18] = { "LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" };
|
||||||
{ "LeftThumbstickUp", true, "LEFT THUMBSTICK UP", ":/help/thumbstick_up.svg" };
|
sGuiInputConfigList[19] = { "RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg" };
|
||||||
sGuiInputConfigList[15] =
|
sGuiInputConfigList[20] = { "RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" };
|
||||||
{ "LeftThumbstickDown", true, "LEFT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" };
|
sGuiInputConfigList[21] = { "RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" };
|
||||||
sGuiInputConfigList[16] =
|
sGuiInputConfigList[22] = { "RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" };
|
||||||
{ "LeftThumbstickLeft", true, "LEFT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" };
|
sGuiInputConfigList[23] = { "RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" };
|
||||||
sGuiInputConfigList[17] =
|
// clang-format on
|
||||||
{ "LeftThumbstickRight", true, "LEFT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" };
|
|
||||||
sGuiInputConfigList[18] =
|
|
||||||
{ "LeftThumbstickClick", true, "LEFT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" };
|
|
||||||
sGuiInputConfigList[19] =
|
|
||||||
{ "RightThumbstickUp", true, "RIGHT THUMBSTICK UP", ":/help/thumbstick_up.svg" };
|
|
||||||
sGuiInputConfigList[20] =
|
|
||||||
{ "RightThumbstickDown", true, "RIGHT THUMBSTICK DOWN", ":/help/thumbstick_down.svg" };
|
|
||||||
sGuiInputConfigList[21] =
|
|
||||||
{ "RightThumbstickLeft", true, "RIGHT THUMBSTICK LEFT", ":/help/thumbstick_left.svg" };
|
|
||||||
sGuiInputConfigList[22] =
|
|
||||||
{ "RightThumbstickRight", true, "RIGHT THUMBSTICK RIGHT", ":/help/thumbstick_right.svg" };
|
|
||||||
sGuiInputConfigList[23] =
|
|
||||||
{ "RightThumbstickClick", true, "RIGHT THUMBSTICK CLICK", ":/help/thumbstick_click.svg" };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiInputConfig::update(int deltaTime)
|
void GuiInputConfig::update(int deltaTime)
|
||||||
|
@ -336,7 +293,7 @@ void GuiInputConfig::update(int deltaTime)
|
||||||
|
|
||||||
void GuiInputConfig::onSizeChanged()
|
void GuiInputConfig::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
|
||||||
// Update grid.
|
// Update grid.
|
||||||
mGrid.setSize(mSize);
|
mGrid.setSize(mSize);
|
||||||
|
@ -400,8 +357,8 @@ bool GuiInputConfig::assign(Input input, int inputId)
|
||||||
// If this input is mapped to something other than "nothing" or the current row,
|
// If this input is mapped to something other than "nothing" or the current row,
|
||||||
// generate an error. (If it's the same as what it was before, allow it.)
|
// generate an error. (If it's the same as what it was before, allow it.)
|
||||||
if (mTargetConfig->getMappedTo(input).size() > 0 &&
|
if (mTargetConfig->getMappedTo(input).size() > 0 &&
|
||||||
!mTargetConfig->isMappedTo(sGuiInputConfigList[inputId].name, input) &&
|
!mTargetConfig->isMappedTo(sGuiInputConfigList[inputId].name, input) &&
|
||||||
sGuiInputConfigList[inputId].name != "HotKeyEnable") {
|
sGuiInputConfigList[inputId].name != "HotKeyEnable") {
|
||||||
error(mMappings.at(inputId), "Already mapped!");
|
error(mMappings.at(inputId), "Already mapped!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -411,8 +368,8 @@ bool GuiInputConfig::assign(Input input, int inputId)
|
||||||
input.configured = true;
|
input.configured = true;
|
||||||
mTargetConfig->mapInput(sGuiInputConfigList[inputId].name, input);
|
mTargetConfig->mapInput(sGuiInputConfigList[inputId].name, input);
|
||||||
|
|
||||||
LOG(LogInfo) << "Mapping [" << input.string() << "] to [" <<
|
LOG(LogInfo) << "Mapping [" << input.string() << "] to [" << sGuiInputConfigList[inputId].name
|
||||||
sGuiInputConfigList[inputId].name << "]";
|
<< "]";
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
#ifndef ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
||||||
#define ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
#define ES_CORE_GUIS_GUI_INPUT_CONFIG_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class ComponentList;
|
class ComponentList;
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
|
@ -19,8 +19,10 @@ class TextComponent;
|
||||||
class GuiInputConfig : public GuiComponent
|
class GuiInputConfig : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiInputConfig(Window* window, InputConfig* target, bool reconfigureAll,
|
GuiInputConfig(Window* window,
|
||||||
const std::function<void()>& okCallback);
|
InputConfig* target,
|
||||||
|
bool reconfigureAll,
|
||||||
|
const std::function<void()>& okCallback);
|
||||||
|
|
||||||
void populateConfigList();
|
void populateConfigList();
|
||||||
|
|
||||||
|
|
|
@ -14,50 +14,55 @@
|
||||||
|
|
||||||
#define HORIZONTAL_PADDING_PX 20
|
#define HORIZONTAL_PADDING_PX 20
|
||||||
|
|
||||||
GuiMsgBox::GuiMsgBox(Window* window, const HelpStyle& helpstyle, const std::string& text,
|
GuiMsgBox::GuiMsgBox(Window* window,
|
||||||
const std::string& name1, const std::function<void()>& func1,
|
const HelpStyle& helpstyle,
|
||||||
const std::string& name2, const std::function<void()>& func2,
|
const std::string& text,
|
||||||
const std::string& name3, const std::function<void()>& func3,
|
const std::string& name1,
|
||||||
bool disableBackButton,
|
const std::function<void()>& func1,
|
||||||
bool deleteOnButtonPress)
|
const std::string& name2,
|
||||||
: GuiComponent(window),
|
const std::function<void()>& func2,
|
||||||
mHelpStyle(helpstyle),
|
const std::string& name3,
|
||||||
mBackground(window, ":/graphics/frame.svg"),
|
const std::function<void()>& func3,
|
||||||
mGrid(window, Vector2i(1, 2)),
|
bool disableBackButton,
|
||||||
mDisableBackButton(disableBackButton),
|
bool deleteOnButtonPress)
|
||||||
mDeleteOnButtonPress(deleteOnButtonPress)
|
: GuiComponent(window)
|
||||||
|
, mHelpStyle(helpstyle)
|
||||||
|
, mBackground(window, ":/graphics/frame.svg")
|
||||||
|
, mGrid(window, Vector2i(1, 2))
|
||||||
|
, mDisableBackButton(disableBackButton)
|
||||||
|
, mDeleteOnButtonPress(deleteOnButtonPress)
|
||||||
{
|
{
|
||||||
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
||||||
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
|
||||||
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
||||||
|
|
||||||
float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) *
|
float width =
|
||||||
Renderer::getScreenWidth());
|
floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * Renderer::getScreenWidth());
|
||||||
float minWidth = floorf(Math::clamp(0.30f * aspectValue, 0.10f, 0.50f) *
|
float minWidth =
|
||||||
Renderer::getScreenWidth());
|
floorf(Math::clamp(0.30f * aspectValue, 0.10f, 0.50f) * Renderer::getScreenWidth());
|
||||||
|
|
||||||
mMsg = std::make_shared<TextComponent>(mWindow, text, Font::get(FONT_SIZE_MEDIUM),
|
mMsg = std::make_shared<TextComponent>(mWindow, text, Font::get(FONT_SIZE_MEDIUM), 0x777777FF,
|
||||||
0x777777FF, ALIGN_CENTER);
|
ALIGN_CENTER);
|
||||||
mGrid.setEntry(mMsg, Vector2i(0, 0), false, false);
|
mGrid.setEntry(mMsg, Vector2i(0, 0), false, false);
|
||||||
|
|
||||||
// Create the buttons.
|
// Create the buttons.
|
||||||
mButtons.push_back(std::make_shared<ButtonComponent>
|
mButtons.push_back(std::make_shared<ButtonComponent>(
|
||||||
(mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1)));
|
mWindow, name1, name1, std::bind(&GuiMsgBox::deleteMeAndCall, this, func1)));
|
||||||
if (!name2.empty())
|
if (!name2.empty())
|
||||||
mButtons.push_back(std::make_shared<ButtonComponent>
|
mButtons.push_back(std::make_shared<ButtonComponent>(
|
||||||
(mWindow, name2, name2, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2)));
|
mWindow, name2, name2, std::bind(&GuiMsgBox::deleteMeAndCall, this, func2)));
|
||||||
if (!name3.empty())
|
if (!name3.empty())
|
||||||
mButtons.push_back(std::make_shared<ButtonComponent>
|
mButtons.push_back(std::make_shared<ButtonComponent>(
|
||||||
(mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3)));
|
mWindow, name3, name3, std::bind(&GuiMsgBox::deleteMeAndCall, this, func3)));
|
||||||
|
|
||||||
// Set accelerator automatically (button to press when "b" is pressed).
|
// Set accelerator automatically (button to press when "B" is pressed).
|
||||||
if (mButtons.size() == 1) {
|
if (mButtons.size() == 1) {
|
||||||
mAcceleratorFunc = mButtons.front()->getPressedFunc();
|
mAcceleratorFunc = mButtons.front()->getPressedFunc();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (auto it = mButtons.cbegin(); it != mButtons.cend(); it++) {
|
for (auto it = mButtons.cbegin(); it != mButtons.cend(); it++) {
|
||||||
if (Utils::String::toUpper((*it)->getText()) == "OK" ||
|
if (Utils::String::toUpper((*it)->getText()) == "OK" ||
|
||||||
Utils::String::toUpper((*it)->getText()) == "NO") {
|
Utils::String::toUpper((*it)->getText()) == "NO") {
|
||||||
mAcceleratorFunc = (*it)->getPressedFunc();
|
mAcceleratorFunc = (*it)->getPressedFunc();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -80,14 +85,14 @@ GuiMsgBox::GuiMsgBox(Window* window, const HelpStyle& helpstyle, const std::stri
|
||||||
|
|
||||||
// Now that we know width, we can find height.
|
// Now that we know width, we can find height.
|
||||||
mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length.
|
mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length.
|
||||||
const float msgHeight = std::max(Font::get(FONT_SIZE_LARGE)->getHeight(),
|
const float msgHeight =
|
||||||
mMsg->getSize().y() * 1.225f);
|
std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), mMsg->getSize().y() * 1.225f);
|
||||||
setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
||||||
msgHeight + mButtonGrid->getSize().y());
|
msgHeight + mButtonGrid->getSize().y());
|
||||||
|
|
||||||
// Center for good measure.
|
// Center for good measure.
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||||
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||||
|
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
@ -103,8 +108,8 @@ void GuiMsgBox::changeText(const std::string& newText)
|
||||||
// reference.
|
// reference.
|
||||||
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
||||||
|
|
||||||
float width = floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) *
|
float width =
|
||||||
Renderer::getScreenWidth());
|
floorf(Math::clamp(0.60f * aspectValue, 0.60f, 0.80f) * Renderer::getScreenWidth());
|
||||||
float minWidth = Renderer::getScreenWidth() * 0.3f;
|
float minWidth = Renderer::getScreenWidth() * 0.3f;
|
||||||
|
|
||||||
// Decide final width.
|
// Decide final width.
|
||||||
|
@ -119,8 +124,8 @@ void GuiMsgBox::changeText(const std::string& newText)
|
||||||
|
|
||||||
// Now that we know width, we can find height.
|
// Now that we know width, we can find height.
|
||||||
mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length.
|
mMsg->setSize(width, 0); // mMsg->getSize.y() now returns the proper length.
|
||||||
const float msgHeight = std::max(Font::get(FONT_SIZE_LARGE)->getHeight(),
|
const float msgHeight =
|
||||||
mMsg->getSize().y() * 1.225f);
|
std::max(Font::get(FONT_SIZE_LARGE)->getHeight(), mMsg->getSize().y() * 1.225f);
|
||||||
setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
setSize(width + HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
||||||
msgHeight + mButtonGrid->getSize().y());
|
msgHeight + mButtonGrid->getSize().y());
|
||||||
}
|
}
|
||||||
|
@ -152,10 +157,10 @@ void GuiMsgBox::onSizeChanged()
|
||||||
|
|
||||||
// Update messagebox size.
|
// Update messagebox size.
|
||||||
mMsg->setSize(mSize.x() - HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
mMsg->setSize(mSize.x() - HORIZONTAL_PADDING_PX * 2 * Renderer::getScreenWidthModifier(),
|
||||||
mGrid.getRowHeight(0));
|
mGrid.getRowHeight(0));
|
||||||
mGrid.onSizeChanged();
|
mGrid.onSizeChanged();
|
||||||
|
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiMsgBox::deleteMeAndCall(const std::function<void()>& func)
|
void GuiMsgBox::deleteMeAndCall(const std::function<void()>& func)
|
||||||
|
|
|
@ -10,9 +10,9 @@
|
||||||
#ifndef ES_CORE_GUIS_GUI_MSG_BOX_H
|
#ifndef ES_CORE_GUIS_GUI_MSG_BOX_H
|
||||||
#define ES_CORE_GUIS_GUI_MSG_BOX_H
|
#define ES_CORE_GUIS_GUI_MSG_BOX_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class ButtonComponent;
|
class ButtonComponent;
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
|
@ -20,18 +20,17 @@ class TextComponent;
|
||||||
class GuiMsgBox : public GuiComponent
|
class GuiMsgBox : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiMsgBox(
|
GuiMsgBox(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
const std::string& text,
|
||||||
const std::string& text,
|
const std::string& name1 = "OK",
|
||||||
const std::string& name1 = "OK",
|
const std::function<void()>& func1 = nullptr,
|
||||||
const std::function<void()>& func1 = nullptr,
|
const std::string& name2 = "",
|
||||||
const std::string& name2 = "",
|
const std::function<void()>& func2 = nullptr,
|
||||||
const std::function<void()>& func2 = nullptr,
|
const std::string& name3 = "",
|
||||||
const std::string& name3 = "",
|
const std::function<void()>& func3 = nullptr,
|
||||||
const std::function<void()>& func3 = nullptr,
|
bool disableBackButton = false,
|
||||||
bool disableBackButton = false,
|
bool deleteOnButtonPress = true);
|
||||||
bool deleteOnButtonPress = true);
|
|
||||||
|
|
||||||
void changeText(const std::string& newText);
|
void changeText(const std::string& newText);
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ public:
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void deleteMeAndCall(const std::function<void()>& func);
|
void deleteMeAndCall(const std::function<void()>& func);
|
||||||
|
|
|
@ -8,58 +8,60 @@
|
||||||
|
|
||||||
#include "guis/GuiTextEditPopup.h"
|
#include "guis/GuiTextEditPopup.h"
|
||||||
|
|
||||||
|
#include "Window.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"
|
||||||
#include "guis/GuiMsgBox.h"
|
#include "guis/GuiMsgBox.h"
|
||||||
#include "Window.h"
|
|
||||||
|
|
||||||
GuiTextEditPopup::GuiTextEditPopup(
|
GuiTextEditPopup::GuiTextEditPopup(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
const std::string& title,
|
||||||
const std::string& title,
|
const std::string& initValue,
|
||||||
const std::string& initValue,
|
const std::function<void(const std::string&)>& okCallback,
|
||||||
const std::function<void(const std::string&)>& okCallback,
|
bool multiLine,
|
||||||
bool multiLine,
|
const std::string& acceptBtnText,
|
||||||
const std::string& acceptBtnText,
|
const std::string& saveConfirmationText)
|
||||||
const std::string& saveConfirmationText)
|
: GuiComponent(window)
|
||||||
: GuiComponent(window),
|
, mHelpStyle(helpstyle)
|
||||||
mHelpStyle(helpstyle),
|
, mBackground(window, ":/graphics/frame.svg")
|
||||||
mBackground(window, ":/graphics/frame.svg"),
|
, mGrid(window, Vector2i(1, 3))
|
||||||
mGrid(window, Vector2i(1, 3)),
|
, mMultiLine(multiLine)
|
||||||
mMultiLine(multiLine),
|
, mInitValue(initValue)
|
||||||
mInitValue(initValue),
|
, mOkCallback(okCallback)
|
||||||
mOkCallback(okCallback),
|
, mSaveConfirmationText(saveConfirmationText)
|
||||||
mSaveConfirmationText(saveConfirmationText)
|
|
||||||
{
|
{
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
|
|
||||||
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
|
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
|
||||||
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
|
||||||
|
|
||||||
mText = std::make_shared<TextEditComponent>(mWindow);
|
mText = std::make_shared<TextEditComponent>(mWindow);
|
||||||
mText->setValue(initValue);
|
mText->setValue(initValue);
|
||||||
|
|
||||||
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
std::vector<std::shared_ptr<ButtonComponent>> buttons;
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
|
||||||
[this, okCallback] { okCallback(mText->getValue()); delete this; }));
|
[this, okCallback] {
|
||||||
|
okCallback(mText->getValue());
|
||||||
|
delete this;
|
||||||
|
}));
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear",
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear",
|
||||||
[this] { mText->setValue(""); }));
|
[this] { mText->setValue(""); }));
|
||||||
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
|
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
|
||||||
[this] { delete this; }));
|
[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(mText, Vector2i(0, 1), true, false, Vector2i(1, 1),
|
mGrid.setEntry(mText, Vector2i(0, 1), true, false, Vector2i(1, 1),
|
||||||
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
|
||||||
mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false);
|
mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false);
|
||||||
|
|
||||||
float textHeight = mText->getFont()->getHeight();
|
float textHeight = mText->getFont()->getHeight();
|
||||||
|
|
||||||
if (multiLine)
|
if (multiLine)
|
||||||
textHeight *= 6;
|
textHeight *= 6.0f;
|
||||||
mText->setSize(0, textHeight);
|
mText->setSize(0, textHeight);
|
||||||
|
|
||||||
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
|
||||||
|
@ -67,16 +69,16 @@ GuiTextEditPopup::GuiTextEditPopup(
|
||||||
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
|
||||||
float width = Math::clamp(0.50f * aspectValue, 0.40f, 0.70f) * Renderer::getScreenWidth();
|
float width = Math::clamp(0.50f * aspectValue, 0.40f, 0.70f) * Renderer::getScreenWidth();
|
||||||
|
|
||||||
setSize(width, mTitle->getFont()->getHeight() +
|
setSize(width, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y() +
|
||||||
textHeight + mButtonGrid->getSize().y() + mButtonGrid->getSize().y() / 2);
|
mButtonGrid->getSize().y() / 2.0f);
|
||||||
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2, (Renderer::getScreenHeight() -
|
setPosition((Renderer::getScreenWidth() - mSize.x()) / 2.0f,
|
||||||
mSize.y()) / 2);
|
(Renderer::getScreenHeight() - mSize.y()) / 2.0f);
|
||||||
mText->startEditing();
|
mText->startEditing();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GuiTextEditPopup::onSizeChanged()
|
void GuiTextEditPopup::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32.0f, -32.0f));
|
||||||
|
|
||||||
mText->setSize(mSize.x() - 40, mText->getSize().y());
|
mText->setSize(mSize.x() - 40, mText->getSize().y());
|
||||||
|
|
||||||
|
@ -95,9 +97,18 @@ bool GuiTextEditPopup::input(InputConfig* config, Input input)
|
||||||
if (config->isMappedTo("b", input) && input.value) {
|
if (config->isMappedTo("b", input) && input.value) {
|
||||||
if (mText->getValue() != mInitValue) {
|
if (mText->getValue() != mInitValue) {
|
||||||
// Changes were made, ask if the user wants to save them.
|
// Changes were made, ask if the user wants to save them.
|
||||||
mWindow->pushGui(new GuiMsgBox(mWindow, mHelpStyle, mSaveConfirmationText, "YES",
|
mWindow->pushGui(new GuiMsgBox(
|
||||||
[this] { this->mOkCallback(mText->getValue()); delete this; return true; },
|
mWindow, mHelpStyle, mSaveConfirmationText, "YES",
|
||||||
"NO", [this] { delete this; return false; }));
|
[this] {
|
||||||
|
this->mOkCallback(mText->getValue());
|
||||||
|
delete this;
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
"NO",
|
||||||
|
[this] {
|
||||||
|
delete this;
|
||||||
|
return false;
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
delete this;
|
delete this;
|
||||||
|
|
|
@ -9,9 +9,9 @@
|
||||||
#ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
|
#ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
|
||||||
#define ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
|
#define ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
|
||||||
|
|
||||||
|
#include "GuiComponent.h"
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
#include "GuiComponent.h"
|
|
||||||
|
|
||||||
class TextComponent;
|
class TextComponent;
|
||||||
class TextEditComponent;
|
class TextEditComponent;
|
||||||
|
@ -19,21 +19,20 @@ class TextEditComponent;
|
||||||
class GuiTextEditPopup : public GuiComponent
|
class GuiTextEditPopup : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GuiTextEditPopup(
|
GuiTextEditPopup(Window* window,
|
||||||
Window* window,
|
const HelpStyle& helpstyle,
|
||||||
const HelpStyle& helpstyle,
|
const std::string& title,
|
||||||
const std::string& title,
|
const std::string& initValue,
|
||||||
const std::string& initValue,
|
const std::function<void(const std::string&)>& okCallback,
|
||||||
const std::function<void(const std::string&)>& okCallback,
|
bool multiLine,
|
||||||
bool multiLine,
|
const std::string& acceptBtnText = "OK",
|
||||||
const std::string& acceptBtnText = "OK",
|
const std::string& saveConfirmationText = "SAVE CHANGES?");
|
||||||
const std::string& saveConfirmationText = "SAVE CHANGES?");
|
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
bool input(InputConfig* config, Input input) override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override;
|
std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
|
|
|
@ -17,20 +17,22 @@ namespace Math
|
||||||
|
|
||||||
float smoothStep(const float _left, const float _right, const float _x)
|
float smoothStep(const float _left, const float _right, const float _x)
|
||||||
{
|
{
|
||||||
const float x = clamp((_x - _left)/(_right - _left), 0.0f, 1.0f);
|
const float x = clamp((_x - _left) / (_right - _left), 0.0f, 1.0f);
|
||||||
return x * x * (3 - (2 * x));
|
return x * x * (3 - (2 * x));
|
||||||
}
|
}
|
||||||
|
|
||||||
float smootherStep(const float _left, const float _right, const float _x)
|
float smootherStep(const float _left, const float _right, const float _x)
|
||||||
{
|
{
|
||||||
const float x = clamp((_x - _left)/(_right - _left), 0.0f, 1.0f);
|
const float x = clamp((_x - _left) / (_right - _left), 0.0f, 1.0f);
|
||||||
return x * x * x * (x * ((x * 6) - 15) + 10);
|
return x * x * x * (x * ((x * 6) - 15) + 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace Scroll
|
namespace Scroll
|
||||||
{
|
{
|
||||||
float bounce(const float _delayTime, const float _scrollTime,
|
float bounce(const float _delayTime,
|
||||||
const float _currentTime, const float _scrollLength)
|
const float _scrollTime,
|
||||||
|
const float _currentTime,
|
||||||
|
const float _scrollLength)
|
||||||
{
|
{
|
||||||
if (_currentTime < _delayTime) {
|
if (_currentTime < _delayTime) {
|
||||||
// Wait.
|
// Wait.
|
||||||
|
@ -47,16 +49,18 @@ namespace Math
|
||||||
}
|
}
|
||||||
else if (_currentTime < (_delayTime + _scrollTime + _delayTime + _scrollTime)) {
|
else if (_currentTime < (_delayTime + _scrollTime + _delayTime + _scrollTime)) {
|
||||||
// Lerp back from scrollLength to 0.
|
// Lerp back from scrollLength to 0.
|
||||||
const float fraction = (_currentTime - _delayTime - _scrollTime -
|
const float fraction =
|
||||||
_delayTime) / _scrollTime;
|
(_currentTime - _delayTime - _scrollTime - _delayTime) / _scrollTime;
|
||||||
return lerp(_scrollLength, 0.0f, smootherStep(0, 1, fraction));
|
return lerp(_scrollLength, 0.0f, smootherStep(0, 1, fraction));
|
||||||
}
|
}
|
||||||
// And back to waiting.
|
// And back to waiting.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
float loop(const float _delayTime, const float _scrollTime,
|
float loop(const float _delayTime,
|
||||||
const float _currentTime, const float _scrollLength)
|
const float _scrollTime,
|
||||||
|
const float _currentTime,
|
||||||
|
const float _scrollLength)
|
||||||
{
|
{
|
||||||
if (_currentTime < _delayTime) {
|
if (_currentTime < _delayTime) {
|
||||||
// Wait.
|
// Wait.
|
||||||
|
@ -72,5 +76,7 @@ namespace Math
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
} // Math::Scroll::loop
|
} // Math::Scroll::loop
|
||||||
} // Math::Scroll::
|
|
||||||
} // Math::
|
} // namespace Scroll
|
||||||
|
|
||||||
|
} // namespace Math
|
||||||
|
|
|
@ -11,15 +11,14 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#define ES_PI (3.1415926535897932384626433832795028841971693993751058209749445923)
|
#define ES_PI (3.1415926535897932384626433832795028841971693993751058209749445923)
|
||||||
#define ES_RAD_TO_DEG(_x) ((_x) * (180.0 / ES_PI))
|
#define ES_RAD_TO_DEG(_x) ((_x) * (180.0 / ES_PI))
|
||||||
#define ES_DEG_TO_RAD(_x) ((_x) * (ES_PI / 180.0))
|
#define ES_DEG_TO_RAD(_x) ((_x) * (ES_PI / 180.0))
|
||||||
|
|
||||||
namespace Math
|
namespace Math
|
||||||
{
|
{
|
||||||
// When moving to the C++20 standard these functions are no longer required.
|
// When moving to the C++20 standard these functions are no longer required.
|
||||||
template<typename T>
|
template <typename T> T const& clamp(const T& _num, const T& _min, const T& _max)
|
||||||
T const& clamp(const T& _num, const T& _min, const T& _max)
|
|
||||||
{
|
{
|
||||||
return std::max(std::min(_num, _max), _min);
|
return std::max(std::min(_num, _max), _min);
|
||||||
}
|
}
|
||||||
|
@ -30,11 +29,16 @@ namespace Math
|
||||||
|
|
||||||
namespace Scroll
|
namespace Scroll
|
||||||
{
|
{
|
||||||
float bounce(const float _delayTime, const float _scrollTime,
|
float bounce(const float _delayTime,
|
||||||
const float _currentTime, const float _scrollLength);
|
const float _scrollTime,
|
||||||
float loop(const float _delayTime, const float _scrollTime,
|
const float _currentTime,
|
||||||
const float _currentTime, const float _scrollLength);
|
const float _scrollLength);
|
||||||
}
|
float loop(const float _delayTime,
|
||||||
}
|
const float _scrollTime,
|
||||||
|
const float _currentTime,
|
||||||
|
const float _scrollLength);
|
||||||
|
} // namespace Scroll
|
||||||
|
|
||||||
|
} // namespace Math
|
||||||
|
|
||||||
#endif // ES_CORE_MATH_MISC_H
|
#endif // ES_CORE_MATH_MISC_H
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const Transform4x4f Transform4x4f::operator*(const Transform4x4f& _other) const
|
const Transform4x4f Transform4x4f::operator*(const Transform4x4f& _other) const
|
||||||
{
|
{
|
||||||
const float* tm = reinterpret_cast<const float*>(this);
|
const float* tm = reinterpret_cast<const float*>(this);
|
||||||
|
@ -321,3 +322,4 @@ Transform4x4f& Transform4x4f::round()
|
||||||
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
|
@ -9,44 +9,43 @@
|
||||||
#ifndef ES_CORE_MATH_TRANSFORM4X4F_H
|
#ifndef ES_CORE_MATH_TRANSFORM4X4F_H
|
||||||
#define ES_CORE_MATH_TRANSFORM4X4F_H
|
#define ES_CORE_MATH_TRANSFORM4X4F_H
|
||||||
|
|
||||||
#include "math/Vector4f.h"
|
|
||||||
#include "math/Vector3f.h"
|
#include "math/Vector3f.h"
|
||||||
|
#include "math/Vector4f.h"
|
||||||
|
|
||||||
class Transform4x4f
|
class Transform4x4f
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Transform4x4f() {}
|
Transform4x4f() {}
|
||||||
Transform4x4f(
|
Transform4x4f(const Vector4f& _r0,
|
||||||
const Vector4f& _r0,
|
const Vector4f& _r1,
|
||||||
const Vector4f& _r1,
|
const Vector4f& _r2,
|
||||||
const Vector4f& _r2,
|
const Vector4f& _r3)
|
||||||
const Vector4f& _r3)
|
: mR0(_r0)
|
||||||
: mR0(_r0),
|
, mR1(_r1)
|
||||||
mR1(_r1),
|
, mR2(_r2)
|
||||||
mR2(_r2),
|
, mR3(_r3)
|
||||||
mR3(_r3) {}
|
{
|
||||||
|
}
|
||||||
|
|
||||||
const Transform4x4f operator*(const Transform4x4f& _other) const;
|
const Transform4x4f operator*(const Transform4x4f& _other) const;
|
||||||
const Vector3f operator*(const Vector3f& _other) const;
|
const Vector3f operator*(const Vector3f& _other) const;
|
||||||
Transform4x4f& operator*=(const Transform4x4f& _other)
|
Transform4x4f& operator*=(const Transform4x4f& _other)
|
||||||
{ *this = *this * _other; return *this; }
|
{
|
||||||
|
*this = *this * _other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
inline Vector4f& r0() { return mR0; }
|
Vector4f& r0() { return mR0; }
|
||||||
inline Vector4f& r1() { return mR1; }
|
Vector4f& r1() { return mR1; }
|
||||||
inline Vector4f& r2() { return mR2; }
|
Vector4f& r2() { return mR2; }
|
||||||
inline Vector4f& r3() { return mR3; }
|
Vector4f& r3() { return mR3; }
|
||||||
inline const Vector4f& r0() const { return mR0; }
|
const Vector4f& r0() const { return mR0; }
|
||||||
inline const Vector4f& r1() const { return mR1; }
|
const Vector4f& r1() const { return mR1; }
|
||||||
inline const Vector4f& r2() const { return mR2; }
|
const Vector4f& r2() const { return mR2; }
|
||||||
inline const Vector4f& r3() const { return mR3; }
|
const Vector4f& r3() const { return mR3; }
|
||||||
|
|
||||||
Transform4x4f& orthoProjection(
|
Transform4x4f& orthoProjection(
|
||||||
float _left,
|
float _left, float _right, float _bottom, float _top, float _near, float _far);
|
||||||
float _right,
|
|
||||||
float _bottom,
|
|
||||||
float _top,
|
|
||||||
float _near,
|
|
||||||
float _far);
|
|
||||||
Transform4x4f& invert(const Transform4x4f& _other);
|
Transform4x4f& invert(const Transform4x4f& _other);
|
||||||
Transform4x4f& scale(const Vector3f& _scale);
|
Transform4x4f& scale(const Vector3f& _scale);
|
||||||
Transform4x4f& rotate(const float _angle, const Vector3f& _axis);
|
Transform4x4f& rotate(const float _angle, const Vector3f& _axis);
|
||||||
|
@ -56,11 +55,13 @@ public:
|
||||||
Transform4x4f& translate(const Vector3f& _translation);
|
Transform4x4f& translate(const Vector3f& _translation);
|
||||||
Transform4x4f& round();
|
Transform4x4f& round();
|
||||||
|
|
||||||
inline Vector3f& translation() { return mR3.v3(); }
|
Vector3f& translation() { return mR3.v3(); }
|
||||||
inline const Vector3f& translation() const { return mR3.v3(); }
|
const Vector3f& translation() const { return mR3.v3(); }
|
||||||
|
|
||||||
static const Transform4x4f Identity()
|
static const Transform4x4f Identity()
|
||||||
{ return { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; }
|
{
|
||||||
|
return { { 1, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } };
|
||||||
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Vector4f mR0;
|
Vector4f mR0;
|
||||||
|
|
|
@ -20,26 +20,41 @@ class Vector2f
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector2f() {}
|
Vector2f() {}
|
||||||
Vector2f(const float _f) : mX(_f), mY(_f) {}
|
Vector2f(const float _f)
|
||||||
Vector2f(const float _x, const float _y) : mX(_x), mY(_y) {}
|
: mX(_f)
|
||||||
explicit Vector2f(const Vector3f& _v) : mX((reinterpret_cast<const Vector2f&>(_v)).mX),
|
, mY(_f)
|
||||||
mY((reinterpret_cast<const Vector2f&>(_v)).mY) {}
|
{
|
||||||
explicit Vector2f(const Vector4f& _v) : mX((reinterpret_cast<const Vector2f&>(_v)).mX),
|
}
|
||||||
mY((reinterpret_cast<const Vector2f&>(_v)).mY) {}
|
Vector2f(const float _x, const float _y)
|
||||||
|
: mX(_x)
|
||||||
|
, mY(_y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit Vector2f(const Vector3f& _v)
|
||||||
|
: mX((reinterpret_cast<const Vector2f&>(_v)).mX)
|
||||||
|
, mY((reinterpret_cast<const Vector2f&>(_v)).mY)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit Vector2f(const Vector4f& _v)
|
||||||
|
: mX((reinterpret_cast<const Vector2f&>(_v)).mX)
|
||||||
|
, mY((reinterpret_cast<const Vector2f&>(_v)).mY)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const bool operator==(const Vector2f& _other) const
|
const bool operator==(const Vector2f& _other) const
|
||||||
{ return ((mX == _other.mX) && (mY == _other.mY)); }
|
{ return ((mX == _other.mX) && (mY == _other.mY)); }
|
||||||
const bool operator!=(const Vector2f& _other) const
|
const bool operator!=(const Vector2f& _other) const
|
||||||
{ return ((mX != _other.mX) || (mY != _other.mY)); }
|
{ return ((mX != _other.mX) || (mY != _other.mY)); }
|
||||||
|
|
||||||
const Vector2f operator+(const Vector2f& _other) const
|
const Vector2f operator+(const Vector2f& _other) const
|
||||||
{ return { mX + _other.mX, mY + _other.mY }; }
|
{ return { mX + _other.mX, mY + _other.mY }; }
|
||||||
const Vector2f operator-(const Vector2f& _other) const
|
const Vector2f operator-(const Vector2f& _other) const
|
||||||
{ return { mX - _other.mX, mY - _other.mY }; }
|
{ return { mX - _other.mX, mY - _other.mY }; }
|
||||||
const Vector2f operator*(const Vector2f& _other) const
|
const Vector2f operator*(const Vector2f& _other) const
|
||||||
{ return { mX * _other.mX, mY * _other.mY }; }
|
{ return { mX * _other.mX, mY * _other.mY }; }
|
||||||
const Vector2f operator/(const Vector2f& _other) const
|
const Vector2f operator/(const Vector2f& _other) const
|
||||||
{ return { mX / _other.mX, mY / _other.mY }; }
|
{ return { mX / _other.mX, mY / _other.mY }; }
|
||||||
|
|
||||||
const Vector2f operator+(const float& _other) const { return { mX + _other, mY + _other }; }
|
const Vector2f operator+(const float& _other) const { return { mX + _other, mY + _other }; }
|
||||||
const Vector2f operator-(const float& _other) const { return { mX - _other, mY - _other }; }
|
const Vector2f operator-(const float& _other) const { return { mX - _other, mY - _other }; }
|
||||||
|
@ -59,21 +74,22 @@ public:
|
||||||
Vector2f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
Vector2f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
||||||
|
|
||||||
float& operator[](const int _index)
|
float& operator[](const int _index)
|
||||||
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
||||||
const float& operator[](const int _index) const
|
const float& operator[](const int _index) const
|
||||||
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
inline float& x() { return mX; }
|
float& x() { return mX; }
|
||||||
inline float& y() { return mY; }
|
float& y() { return mY; }
|
||||||
inline const float& x() const { return mX; }
|
const float& x() const { return mX; }
|
||||||
inline const float& y() const { return mY; }
|
const float& y() const { return mY; }
|
||||||
|
|
||||||
Vector2f& round();
|
Vector2f& round();
|
||||||
Vector2f& lerp (const Vector2f& _start, const Vector2f& _end, const float _fraction);
|
Vector2f& lerp(const Vector2f& _start, const Vector2f& _end, const float _fraction);
|
||||||
|
|
||||||
static const Vector2f Zero() { return { 0, 0 }; }
|
static const Vector2f Zero() { return { 0.0f, 0.0f }; }
|
||||||
static const Vector2f UnitX() { return { 1, 0 }; }
|
static const Vector2f UnitX() { return { 1.0f, 0.0f }; }
|
||||||
static const Vector2f UnitY() { return { 0, 1 }; }
|
static const Vector2f UnitY() { return { 0.0f, 1.0f }; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mX;
|
float mX;
|
||||||
|
|
|
@ -15,22 +15,31 @@ class Vector2i
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector2i() {}
|
Vector2i() {}
|
||||||
Vector2i(const int _i) : mX(_i), mY(_i) {}
|
Vector2i(const int _i)
|
||||||
Vector2i(const int _x, const int _y) : mX(_x), mY(_y) {}
|
: mX(_i)
|
||||||
|
, mY(_i)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Vector2i(const int _x, const int _y)
|
||||||
|
: mX(_x)
|
||||||
|
, mY(_y)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const bool operator==(const Vector2i& _other) const
|
const bool operator==(const Vector2i& _other) const
|
||||||
{ return ((mX == _other.mX) && (mY == _other.mY)); }
|
{ return ((mX == _other.mX) && (mY == _other.mY)); }
|
||||||
const bool operator!=(const Vector2i& _other) const
|
const bool operator!=(const Vector2i& _other) const
|
||||||
{ return ((mX != _other.mX) || (mY != _other.mY)); }
|
{ return ((mX != _other.mX) || (mY != _other.mY)); }
|
||||||
|
|
||||||
const Vector2i operator+(const Vector2i& _other) const
|
const Vector2i operator+(const Vector2i& _other) const
|
||||||
{ return { mX + _other.mX, mY + _other.mY }; }
|
{ return { mX + _other.mX, mY + _other.mY }; }
|
||||||
const Vector2i operator-(const Vector2i& _other) const
|
const Vector2i operator-(const Vector2i& _other) const
|
||||||
{ return { mX - _other.mX, mY - _other.mY }; }
|
{ return { mX - _other.mX, mY - _other.mY }; }
|
||||||
const Vector2i operator*(const Vector2i& _other) const
|
const Vector2i operator*(const Vector2i& _other) const
|
||||||
{ return { mX * _other.mX, mY * _other.mY }; }
|
{ return { mX * _other.mX, mY * _other.mY }; }
|
||||||
const Vector2i operator/(const Vector2i& _other) const
|
const Vector2i operator/(const Vector2i& _other) const
|
||||||
{ return { mX / _other.mX, mY / _other.mY }; }
|
{ return { mX / _other.mX, mY / _other.mY }; }
|
||||||
|
|
||||||
const Vector2i operator+(const int& _other) const { return { mX + _other, mY + _other }; }
|
const Vector2i operator+(const int& _other) const { return { mX + _other, mY + _other }; }
|
||||||
const Vector2i operator-(const int& _other) const { return { mX - _other, mY - _other }; }
|
const Vector2i operator-(const int& _other) const { return { mX - _other, mY - _other }; }
|
||||||
|
@ -50,14 +59,15 @@ public:
|
||||||
Vector2i& operator/=(const int& _other) { *this = *this / _other; return *this; }
|
Vector2i& operator/=(const int& _other) { *this = *this / _other; return *this; }
|
||||||
|
|
||||||
int& operator[](const int _index)
|
int& operator[](const int _index)
|
||||||
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
||||||
const int& operator[](const int _index) const
|
const int& operator[](const int _index) const
|
||||||
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 2 && "index out of range"); return (&mX)[_index]; }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
inline int& x() { return mX; }
|
int& x() { return mX; }
|
||||||
inline int& y() { return mY; }
|
int& y() { return mY; }
|
||||||
inline const int& x() const { return mX; }
|
const int& x() const { return mX; }
|
||||||
inline const int& y() const { return mY; }
|
const int& y() const { return mY; }
|
||||||
|
|
||||||
static const Vector2i Zero() { return { 0, 0 }; }
|
static const Vector2i Zero() { return { 0, 0 }; }
|
||||||
static const Vector2i UnitX() { return { 1, 0 }; }
|
static const Vector2i UnitX() { return { 1, 0 }; }
|
||||||
|
|
|
@ -20,39 +20,60 @@ class Vector3f
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector3f() {}
|
Vector3f() {}
|
||||||
Vector3f(const float _f) : mX(_f), mY(_f), mZ(_f) {}
|
Vector3f(const float _f)
|
||||||
Vector3f(const float _x, const float _y, const float _z) : mX(_x), mY(_y), mZ(_z) {}
|
: mX(_f)
|
||||||
explicit Vector3f(const Vector2f& _v) : mX((reinterpret_cast<const Vector3f&>(_v)).mX),
|
, mY(_f)
|
||||||
mY((reinterpret_cast<const Vector3f&>(_v)).mY), mZ(0) {}
|
, mZ(_f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Vector3f(const float _x, const float _y, const float _z)
|
||||||
|
: mX(_x)
|
||||||
|
, mY(_y)
|
||||||
|
, mZ(_z)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit Vector3f(const Vector2f& _v)
|
||||||
|
: mX((reinterpret_cast<const Vector3f&>(_v)).mX)
|
||||||
|
, mY((reinterpret_cast<const Vector3f&>(_v)).mY)
|
||||||
|
, mZ(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector3f(const Vector2f& _v, const float _z)
|
explicit Vector3f(const Vector2f& _v, const float _z)
|
||||||
: mX((reinterpret_cast<const Vector3f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector3f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector3f&>(_v)).mY), mZ(_z) {}
|
, mY((reinterpret_cast<const Vector3f&>(_v)).mY)
|
||||||
explicit Vector3f(const Vector4f& _v) : mX((reinterpret_cast<const Vector3f&>(_v)).mX),
|
, mZ(_z)
|
||||||
mY((reinterpret_cast<const Vector3f&>(_v)).mY),
|
{
|
||||||
mZ((reinterpret_cast<const Vector3f&>(_v)).mZ) {}
|
}
|
||||||
|
explicit Vector3f(const Vector4f& _v)
|
||||||
|
: mX((reinterpret_cast<const Vector3f&>(_v)).mX)
|
||||||
|
, mY((reinterpret_cast<const Vector3f&>(_v)).mY)
|
||||||
|
, mZ((reinterpret_cast<const Vector3f&>(_v)).mZ)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const bool operator==(const Vector3f& _other) const
|
const bool operator==(const Vector3f& _other) const
|
||||||
{ return ((mX == _other.mX) && (mY == _other.mY) && (mZ == _other.mZ)); }
|
{ return ((mX == _other.mX) && (mY == _other.mY) && (mZ == _other.mZ)); }
|
||||||
const bool operator!=(const Vector3f& _other) const
|
const bool operator!=(const Vector3f& _other) const
|
||||||
{ return ((mX != _other.mX) || (mY != _other.mY) || (mZ != _other.mZ)); }
|
{ return ((mX != _other.mX) || (mY != _other.mY) || (mZ != _other.mZ)); }
|
||||||
|
|
||||||
const Vector3f operator+(const Vector3f& _other) const
|
const Vector3f operator+(const Vector3f& _other) const
|
||||||
{ return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ }; }
|
{ return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ }; }
|
||||||
const Vector3f operator-(const Vector3f& _other) const
|
const Vector3f operator-(const Vector3f& _other) const
|
||||||
{ return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ }; }
|
{ return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ }; }
|
||||||
const Vector3f operator*(const Vector3f& _other) const
|
const Vector3f operator*(const Vector3f& _other) const
|
||||||
{ return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ }; }
|
{ return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ }; }
|
||||||
const Vector3f operator/(const Vector3f& _other) const
|
const Vector3f operator/(const Vector3f& _other) const
|
||||||
{ return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ }; }
|
{ return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ }; }
|
||||||
|
|
||||||
const Vector3f operator+(const float& _other) const
|
const Vector3f operator+(const float& _other) const
|
||||||
{ return { mX + _other, mY + _other, mZ + _other }; }
|
{ return { mX + _other, mY + _other, mZ + _other }; }
|
||||||
const Vector3f operator-(const float& _other) const
|
const Vector3f operator-(const float& _other) const
|
||||||
{ return { mX - _other, mY - _other, mZ - _other }; }
|
{ return { mX - _other, mY - _other, mZ - _other }; }
|
||||||
const Vector3f operator*(const float& _other) const
|
const Vector3f operator*(const float& _other) const
|
||||||
{ return { mX * _other, mY * _other, mZ * _other }; }
|
{ return { mX * _other, mY * _other, mZ * _other }; }
|
||||||
const Vector3f operator/(const float& _other) const
|
const Vector3f operator/(const float& _other) const
|
||||||
{ return { mX / _other, mY / _other, mZ / _other }; }
|
{ return { mX / _other, mY / _other, mZ / _other }; }
|
||||||
|
|
||||||
const Vector3f operator-() const { return { -mX , -mY, -mZ }; }
|
const Vector3f operator-() const { return { -mX , -mY, -mZ }; }
|
||||||
|
|
||||||
|
@ -67,27 +88,28 @@ public:
|
||||||
Vector3f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
Vector3f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
||||||
|
|
||||||
float& operator[](const int _index)
|
float& operator[](const int _index)
|
||||||
{ assert(_index < 3 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 3 && "index out of range"); return (&mX)[_index]; }
|
||||||
const float& operator[](const int _index) const
|
const float& operator[](const int _index) const
|
||||||
{ assert(_index < 3 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 3 && "index out of range"); return (&mX)[_index]; }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
inline float& x() { return mX; }
|
float& x() { return mX; }
|
||||||
inline float& y() { return mY; }
|
float& y() { return mY; }
|
||||||
inline float& z() { return mZ; }
|
float& z() { return mZ; }
|
||||||
inline const float& x() const { return mX; }
|
const float& x() const { return mX; }
|
||||||
inline const float& y() const { return mY; }
|
const float& y() const { return mY; }
|
||||||
inline const float& z() const { return mZ; }
|
const float& z() const { return mZ; }
|
||||||
|
|
||||||
inline Vector2f& v2() { return *reinterpret_cast<Vector2f*>(this); }
|
Vector2f& v2() { return *reinterpret_cast<Vector2f*>(this); }
|
||||||
inline const Vector2f& v2() const { return *reinterpret_cast<const Vector2f*>(this); }
|
const Vector2f& v2() const { return *reinterpret_cast<const Vector2f*>(this); }
|
||||||
|
|
||||||
Vector3f& round();
|
Vector3f& round();
|
||||||
Vector3f& lerp(const Vector3f& _start, const Vector3f& _end, const float _fraction);
|
Vector3f& lerp(const Vector3f& _start, const Vector3f& _end, const float _fraction);
|
||||||
|
|
||||||
static const Vector3f Zero() { return { 0, 0, 0 }; }
|
static const Vector3f Zero() { return { 0.0f, 0.0f, 0.0f }; }
|
||||||
static const Vector3f UnitX() { return { 1, 0, 0 }; }
|
static const Vector3f UnitX() { return { 1.0f, 0.0f, 0.0f }; }
|
||||||
static const Vector3f UnitY() { return { 0, 1, 0 }; }
|
static const Vector3f UnitY() { return { 0.0f, 1.0f, 0.0f }; }
|
||||||
static const Vector3f UnitZ() { return { 0, 0, 1 }; }
|
static const Vector3f UnitZ() { return { 0.0f, 0.0f, 1.0f }; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mX;
|
float mX;
|
||||||
|
|
|
@ -20,51 +20,81 @@ class Vector4f
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Vector4f() {}
|
Vector4f() {}
|
||||||
Vector4f(const float _f) : mX(_f), mY(_f), mZ(_f), mW(_f) {}
|
Vector4f(const float _f)
|
||||||
|
: mX(_f)
|
||||||
|
, mY(_f)
|
||||||
|
, mZ(_f)
|
||||||
|
, mW(_f)
|
||||||
|
{
|
||||||
|
}
|
||||||
Vector4f(const float _x, const float _y, const float _z, const float _w)
|
Vector4f(const float _x, const float _y, const float _z, const float _w)
|
||||||
: mX(_x), mY(_y), mZ(_z), mW(_w) {}
|
: mX(_x)
|
||||||
|
, mY(_y)
|
||||||
|
, mZ(_z)
|
||||||
|
, mW(_w)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector4f(const Vector2f& _v)
|
explicit Vector4f(const Vector2f& _v)
|
||||||
: mX((reinterpret_cast<const Vector4f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector4f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector4f&>(_v)).mY), mZ(0), mW(0) {}
|
, mY((reinterpret_cast<const Vector4f&>(_v)).mY)
|
||||||
|
, mZ(0)
|
||||||
|
, mW(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector4f(const Vector2f& _v, const float _z)
|
explicit Vector4f(const Vector2f& _v, const float _z)
|
||||||
: mX((reinterpret_cast<const Vector4f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector4f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector4f&>(_v)).mY), mZ(_z), mW(0) {}
|
, mY((reinterpret_cast<const Vector4f&>(_v)).mY)
|
||||||
|
, mZ(_z)
|
||||||
|
, mW(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector4f(const Vector2f& _v, const float _z, const float _w)
|
explicit Vector4f(const Vector2f& _v, const float _z, const float _w)
|
||||||
: mX((reinterpret_cast<const Vector4f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector4f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector4f&>(_v)).mY), mZ(_z), mW(_w) {}
|
, mY((reinterpret_cast<const Vector4f&>(_v)).mY)
|
||||||
|
, mZ(_z)
|
||||||
|
, mW(_w)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector4f(const Vector3f& _v)
|
explicit Vector4f(const Vector3f& _v)
|
||||||
: mX((reinterpret_cast<const Vector4f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector4f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector4f&>(_v)).mY),
|
, mY((reinterpret_cast<const Vector4f&>(_v)).mY)
|
||||||
mZ((reinterpret_cast<const Vector4f&>(_v)).mZ), mW(0) {}
|
, mZ((reinterpret_cast<const Vector4f&>(_v)).mZ)
|
||||||
|
, mW(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
explicit Vector4f(const Vector3f& _v, const float _w)
|
explicit Vector4f(const Vector3f& _v, const float _w)
|
||||||
: mX((reinterpret_cast<const Vector4f&>(_v)).mX),
|
: mX((reinterpret_cast<const Vector4f&>(_v)).mX)
|
||||||
mY((reinterpret_cast<const Vector4f&>(_v)).mY),
|
, mY((reinterpret_cast<const Vector4f&>(_v)).mY)
|
||||||
mZ((reinterpret_cast<const Vector4f&>(_v)).mZ), mW(_w) {}
|
, mZ((reinterpret_cast<const Vector4f&>(_v)).mZ)
|
||||||
|
, mW(_w)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
const bool operator==(const Vector4f& _other) const
|
const bool operator==(const Vector4f& _other) const
|
||||||
{ return ((mX == _other.mX) && (mY == _other.mY) &&
|
{ return ((mX == _other.mX) && (mY == _other.mY) &&
|
||||||
(mZ == _other.mZ) && (mW == _other.mW)); }
|
(mZ == _other.mZ) && (mW == _other.mW)); }
|
||||||
const bool operator!=(const Vector4f& _other) const
|
const bool operator!=(const Vector4f& _other) const
|
||||||
{ return ((mX != _other.mX) || (mY != _other.mY) ||
|
{ return ((mX != _other.mX) || (mY != _other.mY) ||
|
||||||
(mZ != _other.mZ) || (mW != _other.mW)); }
|
(mZ != _other.mZ) || (mW != _other.mW)); }
|
||||||
|
|
||||||
const Vector4f operator+(const Vector4f& _other) const
|
const Vector4f operator+(const Vector4f& _other) const
|
||||||
{ return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ, mW + _other.mW }; }
|
{ return { mX + _other.mX, mY + _other.mY, mZ + _other.mZ, mW + _other.mW }; }
|
||||||
const Vector4f operator-(const Vector4f& _other) const
|
const Vector4f operator-(const Vector4f& _other) const
|
||||||
{ return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ, mW - _other.mW }; }
|
{ return { mX - _other.mX, mY - _other.mY, mZ - _other.mZ, mW - _other.mW }; }
|
||||||
const Vector4f operator*(const Vector4f& _other) const
|
const Vector4f operator*(const Vector4f& _other) const
|
||||||
{ return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ, mW * _other.mW }; }
|
{ return { mX * _other.mX, mY * _other.mY, mZ * _other.mZ, mW * _other.mW }; }
|
||||||
const Vector4f operator/(const Vector4f& _other) const
|
const Vector4f operator/(const Vector4f& _other) const
|
||||||
{ return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ, mW / _other.mW }; }
|
{ return { mX / _other.mX, mY / _other.mY, mZ / _other.mZ, mW / _other.mW }; }
|
||||||
|
|
||||||
const Vector4f operator+(const float& _other) const
|
const Vector4f operator+(const float& _other) const
|
||||||
{ return { mX + _other, mY + _other, mZ + _other, mW + _other }; }
|
{ return { mX + _other, mY + _other, mZ + _other, mW + _other }; }
|
||||||
const Vector4f operator-(const float& _other) const
|
const Vector4f operator-(const float& _other) const
|
||||||
{ return { mX - _other, mY - _other, mZ - _other, mW - _other }; }
|
{ return { mX - _other, mY - _other, mZ - _other, mW - _other }; }
|
||||||
const Vector4f operator*(const float& _other) const
|
const Vector4f operator*(const float& _other) const
|
||||||
{ return { mX * _other, mY * _other, mZ * _other, mW * _other }; }
|
{ return { mX * _other, mY * _other, mZ * _other, mW * _other }; }
|
||||||
const Vector4f operator/(const float& _other) const
|
const Vector4f operator/(const float& _other) const
|
||||||
{ return { mX / _other, mY / _other, mZ / _other, mW / _other }; }
|
{ return { mX / _other, mY / _other, mZ / _other, mW / _other }; }
|
||||||
|
|
||||||
const Vector4f operator-() const { return {-mX , -mY, -mZ, -mW }; }
|
const Vector4f operator-() const { return {-mX , -mY, -mZ, -mW }; }
|
||||||
|
|
||||||
|
@ -79,33 +109,34 @@ public:
|
||||||
Vector4f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
Vector4f& operator/=(const float& _other) { *this = *this / _other; return *this; }
|
||||||
|
|
||||||
float& operator[](const int _index)
|
float& operator[](const int _index)
|
||||||
{ assert(_index < 4 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 4 && "index out of range"); return (&mX)[_index]; }
|
||||||
const float& operator[](const int _index) const
|
const float& operator[](const int _index) const
|
||||||
{ assert(_index < 4 && "index out of range"); return (&mX)[_index]; }
|
{ assert(_index < 4 && "index out of range"); return (&mX)[_index]; }
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
inline float& x() { return mX; }
|
float& x() { return mX; }
|
||||||
inline float& y() { return mY; }
|
float& y() { return mY; }
|
||||||
inline float& z() { return mZ; }
|
float& z() { return mZ; }
|
||||||
inline float& w() { return mW; }
|
float& w() { return mW; }
|
||||||
inline const float& x() const { return mX; }
|
const float& x() const { return mX; }
|
||||||
inline const float& y() const { return mY; }
|
const float& y() const { return mY; }
|
||||||
inline const float& z() const { return mZ; }
|
const float& z() const { return mZ; }
|
||||||
inline const float& w() const { return mW; }
|
const float& w() const { return mW; }
|
||||||
|
|
||||||
inline Vector2f& v2() { return *reinterpret_cast<Vector2f*>(this); }
|
Vector2f& v2() { return *reinterpret_cast<Vector2f*>(this); }
|
||||||
inline const Vector2f& v2() const { return *reinterpret_cast<const Vector2f*>(this); }
|
const Vector2f& v2() const { return *reinterpret_cast<const Vector2f*>(this); }
|
||||||
|
|
||||||
inline Vector3f& v3() { return *reinterpret_cast<Vector3f*>(this); }
|
Vector3f& v3() { return *reinterpret_cast<Vector3f*>(this); }
|
||||||
inline const Vector3f& v3() const { return *reinterpret_cast<const Vector3f*>(this); }
|
const Vector3f& v3() const { return *reinterpret_cast<const Vector3f*>(this); }
|
||||||
|
|
||||||
Vector4f& round();
|
Vector4f& round();
|
||||||
Vector4f& lerp (const Vector4f& _start, const Vector4f& _end, const float _fraction);
|
Vector4f& lerp(const Vector4f& _start, const Vector4f& _end, const float _fraction);
|
||||||
|
|
||||||
static const Vector4f Zero() { return { 0, 0, 0, 0 }; }
|
static const Vector4f Zero() { return { 0.0f, 0.0f, 0.0f, 0.0f }; }
|
||||||
static const Vector4f UnitX() { return { 1, 0, 0, 0 }; }
|
static const Vector4f UnitX() { return { 1.0f, 0.0f, 0.0f, 0.0f }; }
|
||||||
static const Vector4f UnitY() { return { 0, 1, 0, 0 }; }
|
static const Vector4f UnitY() { return { 0.0f, 1.0f, 0.0f, 0.0f }; }
|
||||||
static const Vector4f UnitZ() { return { 0, 0, 1, 0 }; }
|
static const Vector4f UnitZ() { return { 0.0f, 0.0f, 1.0f, 0.0f }; }
|
||||||
static const Vector4f UnitW() { return { 0, 0, 0, 1 }; }
|
static const Vector4f UnitW() { return { 0.0f, 0.0f, 0.0f, 1.0f }; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
float mX;
|
float mX;
|
||||||
|
|
|
@ -8,18 +8,18 @@
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
#include "math/Transform4x4f.h"
|
|
||||||
#include "math/Vector2i.h"
|
|
||||||
#include "resources/ResourceManager.h"
|
|
||||||
#include "ImageIO.h"
|
#include "ImageIO.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Shader_GL21.h"
|
#include "Shader_GL21.h"
|
||||||
|
#include "math/Transform4x4f.h"
|
||||||
|
#include "math/Vector2i.h"
|
||||||
|
#include "resources/ResourceManager.h"
|
||||||
|
|
||||||
#include <SDL2/SDL.h>
|
#include <SDL2/SDL.h>
|
||||||
#include <stack>
|
#include <stack>
|
||||||
|
|
||||||
#if defined (_WIN64)
|
#if defined(_WIN64)
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -44,31 +44,31 @@ namespace Renderer
|
||||||
{
|
{
|
||||||
size_t width = 0;
|
size_t width = 0;
|
||||||
size_t height = 0;
|
size_t height = 0;
|
||||||
ResourceData resData = ResourceManager::getInstance()->
|
ResourceData resData =
|
||||||
getFileData(":/graphics/window_icon_256.png");
|
ResourceManager::getInstance()->getFileData(":/graphics/window_icon_256.png");
|
||||||
std::vector<unsigned char> rawData =
|
std::vector<unsigned char> rawData =
|
||||||
ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height);
|
ImageIO::loadFromMemoryRGBA32(resData.ptr.get(), resData.length, width, height);
|
||||||
|
|
||||||
if (!rawData.empty()) {
|
if (!rawData.empty()) {
|
||||||
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
ImageIO::flipPixelsVert(rawData.data(), width, height);
|
||||||
|
|
||||||
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
#if SDL_BYTEORDER == SDL_BIG_ENDIAN
|
||||||
unsigned int rmask = 0xFF000000;
|
unsigned int rmask = 0xFF000000;
|
||||||
unsigned int gmask = 0x00FF0000;
|
unsigned int gmask = 0x00FF0000;
|
||||||
unsigned int bmask = 0x0000FF00;
|
unsigned int bmask = 0x0000FF00;
|
||||||
unsigned int amask = 0x000000FF;
|
unsigned int amask = 0x000000FF;
|
||||||
#else
|
#else
|
||||||
unsigned int rmask = 0x000000FF;
|
unsigned int rmask = 0x000000FF;
|
||||||
unsigned int gmask = 0x0000FF00;
|
unsigned int gmask = 0x0000FF00;
|
||||||
unsigned int bmask = 0x00FF0000;
|
unsigned int bmask = 0x00FF0000;
|
||||||
unsigned int amask = 0xFF000000;
|
unsigned int amask = 0xFF000000;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Try creating SDL surface from logo data.
|
// Try creating SDL surface from logo data.
|
||||||
SDL_Surface* logoSurface = SDL_CreateRGBSurfaceFrom(
|
SDL_Surface* logoSurface =
|
||||||
static_cast<void*>(rawData.data()),
|
SDL_CreateRGBSurfaceFrom(static_cast<void*>(rawData.data()),
|
||||||
static_cast<int>(width), static_cast<int>(height), 32,
|
static_cast<int>(width), static_cast<int>(height), 32,
|
||||||
static_cast<int>((width * 4)), rmask, gmask, bmask, amask);
|
static_cast<int>((width * 4)), rmask, gmask, bmask, amask);
|
||||||
|
|
||||||
if (logoSurface != nullptr) {
|
if (logoSurface != nullptr) {
|
||||||
SDL_SetWindowIcon(sdlWindow, logoSurface);
|
SDL_SetWindowIcon(sdlWindow, logoSurface);
|
||||||
|
@ -100,8 +100,8 @@ namespace Renderer
|
||||||
|
|
||||||
int availableDisplays = SDL_GetNumVideoDisplays();
|
int availableDisplays = SDL_GetNumVideoDisplays();
|
||||||
if (displayIndex > availableDisplays - 1) {
|
if (displayIndex > availableDisplays - 1) {
|
||||||
LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1) <<
|
LOG(LogWarning) << "Requested display " << std::to_string(displayIndex + 1)
|
||||||
" does not exist, changing to display 1";
|
<< " does not exist, changing to display 1";
|
||||||
displayIndex = 0;
|
displayIndex = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -111,7 +111,7 @@ namespace Renderer
|
||||||
SDL_DisplayMode displayMode;
|
SDL_DisplayMode displayMode;
|
||||||
SDL_GetDesktopDisplayMode(displayIndex, &displayMode);
|
SDL_GetDesktopDisplayMode(displayIndex, &displayMode);
|
||||||
|
|
||||||
#if defined (_WIN64)
|
#if defined(_WIN64)
|
||||||
// Tell Windows that we're DPI aware so that we can set a physical resolution and
|
// Tell Windows that we're DPI aware so that we can set a physical resolution and
|
||||||
// avoid any automatic DPI scaling.
|
// avoid any automatic DPI scaling.
|
||||||
SetProcessDPIAware();
|
SetProcessDPIAware();
|
||||||
|
@ -122,28 +122,35 @@ namespace Renderer
|
||||||
SDL_GetDisplayBounds(displayIndex, &displayBounds);
|
SDL_GetDisplayBounds(displayIndex, &displayBounds);
|
||||||
displayMode.w = displayBounds.w;
|
displayMode.w = displayBounds.w;
|
||||||
displayMode.h = displayBounds.h;
|
displayMode.h = displayBounds.h;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
windowWidth = Settings::getInstance()->getInt("WindowWidth") ?
|
windowWidth = Settings::getInstance()->getInt("WindowWidth") ?
|
||||||
Settings::getInstance()->getInt("WindowWidth") : displayMode.w;
|
Settings::getInstance()->getInt("WindowWidth") :
|
||||||
|
displayMode.w;
|
||||||
windowHeight = Settings::getInstance()->getInt("WindowHeight") ?
|
windowHeight = Settings::getInstance()->getInt("WindowHeight") ?
|
||||||
Settings::getInstance()->getInt("WindowHeight") : displayMode.h;
|
Settings::getInstance()->getInt("WindowHeight") :
|
||||||
|
displayMode.h;
|
||||||
screenWidth = Settings::getInstance()->getInt("ScreenWidth") ?
|
screenWidth = Settings::getInstance()->getInt("ScreenWidth") ?
|
||||||
Settings::getInstance()->getInt("ScreenWidth") : windowWidth;
|
Settings::getInstance()->getInt("ScreenWidth") :
|
||||||
|
windowWidth;
|
||||||
screenHeight = Settings::getInstance()->getInt("ScreenHeight") ?
|
screenHeight = Settings::getInstance()->getInt("ScreenHeight") ?
|
||||||
Settings::getInstance()->getInt("ScreenHeight") : windowHeight;
|
Settings::getInstance()->getInt("ScreenHeight") :
|
||||||
|
windowHeight;
|
||||||
screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ?
|
screenOffsetX = Settings::getInstance()->getInt("ScreenOffsetX") ?
|
||||||
Settings::getInstance()->getInt("ScreenOffsetX") : 0;
|
Settings::getInstance()->getInt("ScreenOffsetX") :
|
||||||
|
0;
|
||||||
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
|
screenOffsetY = Settings::getInstance()->getInt("ScreenOffsetY") ?
|
||||||
Settings::getInstance()->getInt("ScreenOffsetY") : 0;
|
Settings::getInstance()->getInt("ScreenOffsetY") :
|
||||||
|
0;
|
||||||
screenRotate = Settings::getInstance()->getInt("ScreenRotate") ?
|
screenRotate = Settings::getInstance()->getInt("ScreenRotate") ?
|
||||||
Settings::getInstance()->getInt("ScreenRotate") : 0;
|
Settings::getInstance()->getInt("ScreenRotate") :
|
||||||
|
0;
|
||||||
|
|
||||||
// Prevent the application window from minimizing when switching windows (when launching
|
// Prevent the application window from minimizing when switching windows (when launching
|
||||||
// games or when manually switching windows using the task switcher).
|
// games or when manually switching windows using the task switcher).
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
SDL_SetHint(SDL_HINT_VIDEO_MINIMIZE_ON_FOCUS_LOSS, "0");
|
||||||
|
|
||||||
#if defined(__unix__)
|
#if defined(__unix__)
|
||||||
// Disabling desktop composition can lead to better framerates and a more fluid user
|
// Disabling desktop composition can lead to better framerates and a more fluid user
|
||||||
// interface, but with some drivers it can cause strange behaviours when returning to
|
// interface, but with some drivers it can cause strange behaviours when returning to
|
||||||
// the desktop.
|
// the desktop.
|
||||||
|
@ -151,29 +158,28 @@ namespace Renderer
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1");
|
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "1");
|
||||||
else
|
else
|
||||||
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
|
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(__APPLE__) || defined(__unix__)
|
#if defined(__APPLE__) || defined(__unix__)
|
||||||
bool userResolution = false;
|
bool userResolution = false;
|
||||||
// Check if the user has changed the resolution from the command line.
|
// Check if the user has changed the resolution from the command line.
|
||||||
if (windowWidth != displayMode.w || windowHeight != displayMode.h)
|
if (windowWidth != displayMode.w || windowHeight != displayMode.h)
|
||||||
userResolution = true;
|
userResolution = true;
|
||||||
// Not sure if this could be a useful setting for some users.
|
// Not sure if this could be a useful setting for some users.
|
||||||
// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
// SDL_SetHint(SDL_HINT_VIDEO_MAC_FULLSCREEN_SPACES, "0");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
setupWindow();
|
|
||||||
|
|
||||||
unsigned int windowFlags;
|
unsigned int windowFlags;
|
||||||
|
setupWindow();
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
// For Windows, always set the mode to windowed, as full screen mode seems to
|
// For Windows, always set the mode to windowed, as full screen mode seems to
|
||||||
// behave quite erratic. There may be a proper fix for this, but for now windowed
|
// behave quite erratic. There may be a proper fix for this, but for now windowed
|
||||||
// mode seems to behave well and it's almost completely seamless, especially with
|
// mode seems to behave well and it's almost completely seamless, especially with
|
||||||
// a hidden taskbar. As well, setting SDL_WINDOW_BORDERLESS introduces issues too
|
// a hidden taskbar. As well, setting SDL_WINDOW_BORDERLESS introduces issues too
|
||||||
// so unfortunately this needs to be avoided.
|
// so unfortunately this needs to be avoided.
|
||||||
windowFlags = getWindowFlags();
|
windowFlags = getWindowFlags();
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
// This seems to be the only full window mode that somehow works on macOS as a real
|
// This seems to be the only full window mode that somehow works on macOS as a real
|
||||||
// fullscreen mode will do lots of weird stuff like preventing window switching
|
// fullscreen mode will do lots of weird stuff like preventing window switching
|
||||||
// or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost
|
// or refusing to let emulators run at all. SDL_WINDOW_FULLSCREEN_DESKTOP almost
|
||||||
|
@ -190,7 +196,7 @@ namespace Renderer
|
||||||
// If the user has changed the resolution from the command line, then add a
|
// If the user has changed the resolution from the command line, then add a
|
||||||
// border to the window.
|
// border to the window.
|
||||||
windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | getWindowFlags();
|
windowFlags = SDL_WINDOW_ALLOW_HIGHDPI | getWindowFlags();
|
||||||
#else
|
#else
|
||||||
if (Settings::getInstance()->getBool("Windowed")) {
|
if (Settings::getInstance()->getBool("Windowed")) {
|
||||||
windowFlags = getWindowFlags();
|
windowFlags = getWindowFlags();
|
||||||
}
|
}
|
||||||
|
@ -205,17 +211,17 @@ namespace Renderer
|
||||||
else {
|
else {
|
||||||
windowFlags = SDL_WINDOW_FULLSCREEN | getWindowFlags();
|
windowFlags = SDL_WINDOW_FULLSCREEN | getWindowFlags();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if ((sdlWindow = SDL_CreateWindow("EmulationStation",
|
if ((sdlWindow =
|
||||||
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
SDL_CreateWindow("EmulationStation", SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
||||||
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex),
|
SDL_WINDOWPOS_UNDEFINED_DISPLAY(displayIndex), windowWidth,
|
||||||
windowWidth, windowHeight, windowFlags)) == nullptr) {
|
windowHeight, windowFlags)) == nullptr) {
|
||||||
LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError();
|
LOG(LogError) << "Couldn't create SDL window. " << SDL_GetError();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// The code below is required as the high DPI scaling on macOS is very bizarre and is
|
// The code below is required as the high DPI scaling on macOS is very bizarre and is
|
||||||
// measured in "points" rather than pixels (even though the naming convention sure looks
|
// measured in "points" rather than pixels (even though the naming convention sure looks
|
||||||
// like pixels). For example there could be a 1920x1080 entry in the OS display settings
|
// like pixels). For example there could be a 1920x1080 entry in the OS display settings
|
||||||
|
@ -231,25 +237,25 @@ namespace Renderer
|
||||||
SDL_GL_GetDrawableSize(sdlWindow, &width, nullptr);
|
SDL_GL_GetDrawableSize(sdlWindow, &width, nullptr);
|
||||||
int scaleFactor = static_cast<int>(width / windowWidth);
|
int scaleFactor = static_cast<int>(width / windowWidth);
|
||||||
|
|
||||||
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" <<
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x"
|
||||||
std::to_string(displayMode.h) << " (physical resolution " <<
|
<< std::to_string(displayMode.h) << " (physical resolution "
|
||||||
std::to_string(displayMode.w * scaleFactor) << "x" <<
|
<< std::to_string(displayMode.w * scaleFactor) << "x"
|
||||||
std::to_string(displayMode.h * scaleFactor) << ")";
|
<< std::to_string(displayMode.h * scaleFactor) << ")";
|
||||||
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" <<
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x"
|
||||||
std::to_string(windowHeight) << " (physical resolution " <<
|
<< std::to_string(windowHeight) << " (physical resolution "
|
||||||
std::to_string(windowWidth * scaleFactor) << "x" <<
|
<< std::to_string(windowWidth * scaleFactor) << "x"
|
||||||
std::to_string(windowHeight * scaleFactor) << ")";
|
<< std::to_string(windowHeight * scaleFactor) << ")";
|
||||||
|
|
||||||
windowWidth *= scaleFactor;
|
windowWidth *= scaleFactor;
|
||||||
windowHeight *= scaleFactor;
|
windowHeight *= scaleFactor;
|
||||||
screenWidth *= scaleFactor;
|
screenWidth *= scaleFactor;
|
||||||
screenHeight *= scaleFactor;
|
screenHeight *= scaleFactor;
|
||||||
#else
|
#else
|
||||||
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x" <<
|
LOG(LogInfo) << "Display resolution: " << std::to_string(displayMode.w) << "x"
|
||||||
std::to_string(displayMode.h);
|
<< std::to_string(displayMode.h);
|
||||||
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x" <<
|
LOG(LogInfo) << "EmulationStation resolution: " << std::to_string(windowWidth) << "x"
|
||||||
std::to_string(windowHeight);
|
<< std::to_string(windowHeight);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
screenHeightModifier = static_cast<float>(screenHeight) / 1080.0f;
|
screenHeightModifier = static_cast<float>(screenHeight) / 1080.0f;
|
||||||
screenWidthModifier = static_cast<float>(screenWidth) / 1920.0f;
|
screenWidthModifier = static_cast<float>(screenWidth) / 1920.0f;
|
||||||
|
@ -263,14 +269,14 @@ namespace Renderer
|
||||||
setIcon();
|
setIcon();
|
||||||
setSwapInterval();
|
setSwapInterval();
|
||||||
|
|
||||||
|
#if defined(_WIN64)
|
||||||
// It seems as if Windows needs this to avoid a brief white screen flash on startup.
|
// It seems as if Windows needs this to avoid a brief white screen flash on startup.
|
||||||
// Possibly this is driver-specific rather than OS-specific. There is additional code
|
// Possibly this is driver-specific rather than OS-specific. There is additional code
|
||||||
// in init() to work around the white screen flash issue on all operating systems.
|
// in init() to work around the white screen flash issue on all operating systems.
|
||||||
#if defined(_WIN64)
|
|
||||||
swapBuffers();
|
swapBuffers();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
LOG(LogInfo) << "Loading shaders...";
|
LOG(LogInfo) << "Loading shaders...";
|
||||||
|
|
||||||
std::vector<std::string> shaderFiles;
|
std::vector<std::string> shaderFiles;
|
||||||
|
@ -294,19 +300,17 @@ namespace Renderer
|
||||||
|
|
||||||
sShaderProgramVector.push_back(loadShader);
|
sShaderProgramVector.push_back(loadShader);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroyWindow()
|
static void destroyWindow()
|
||||||
{
|
{
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
for (auto it = sShaderProgramVector.cbegin();
|
for (auto it = sShaderProgramVector.cbegin(); it != sShaderProgramVector.cend(); it++)
|
||||||
it != sShaderProgramVector.cend(); it++) {
|
|
||||||
delete *it;
|
delete *it;
|
||||||
}
|
#endif
|
||||||
#endif
|
|
||||||
|
|
||||||
destroyContext();
|
destroyContext();
|
||||||
SDL_DestroyWindow(sdlWindow);
|
SDL_DestroyWindow(sdlWindow);
|
||||||
|
@ -332,42 +336,42 @@ namespace Renderer
|
||||||
viewport.w = screenWidth;
|
viewport.w = screenWidth;
|
||||||
viewport.h = screenHeight;
|
viewport.h = screenHeight;
|
||||||
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
||||||
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 1: {
|
case 1: {
|
||||||
viewport.x = windowWidth - screenOffsetY - screenHeight;
|
viewport.x = windowWidth - screenOffsetY - screenHeight;
|
||||||
viewport.y = screenOffsetX;
|
viewport.y = screenOffsetX;
|
||||||
viewport.w = screenHeight;
|
viewport.w = screenHeight;
|
||||||
viewport.h = screenWidth;
|
viewport.h = screenWidth;
|
||||||
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
||||||
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
||||||
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(90)), {0, 0, 1});
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(90)), { 0, 0, 1 });
|
||||||
projection.translate({0, screenHeight * -1.0f, 0});
|
projection.translate({ 0, screenHeight * -1.0f, 0 });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 2: {
|
case 2: {
|
||||||
viewport.x = windowWidth - screenOffsetX - screenWidth;
|
viewport.x = windowWidth - screenOffsetX - screenWidth;
|
||||||
viewport.y = windowHeight - screenOffsetY - screenHeight;
|
viewport.y = windowHeight - screenOffsetY - screenHeight;
|
||||||
viewport.w = screenWidth;
|
viewport.w = screenWidth;
|
||||||
viewport.h = screenHeight;
|
viewport.h = screenHeight;
|
||||||
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
projection.orthoProjection(0.0f, static_cast<float>(screenWidth),
|
||||||
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
static_cast<float>(screenHeight), 0.0f, -1.0f, 1.0f);
|
||||||
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(180)), {0, 0, 1});
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(180)), { 0, 0, 1 });
|
||||||
projection.translate({screenWidth * -1.0f, screenHeight * -1.0f, 0});
|
projection.translate({ screenWidth * -1.0f, screenHeight * -1.0f, 0 });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
case 3: {
|
case 3: {
|
||||||
viewport.x = screenOffsetY;
|
viewport.x = screenOffsetY;
|
||||||
viewport.y = windowHeight - screenOffsetX - screenWidth;
|
viewport.y = windowHeight - screenOffsetX - screenWidth;
|
||||||
viewport.w = screenHeight;
|
viewport.w = screenHeight;
|
||||||
viewport.h = screenWidth;
|
viewport.h = screenWidth;
|
||||||
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
projection.orthoProjection(0.0f, static_cast<float>(screenHeight),
|
||||||
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
static_cast<float>(screenWidth), 0.0f, -1.0f, 1.0f);
|
||||||
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(270)), {0, 0, 1});
|
projection.rotate(static_cast<float>(ES_DEG_TO_RAD(270)), { 0, 0, 1 });
|
||||||
projection.translate({screenWidth * -1.0f, 0, 0});
|
projection.translate({ screenWidth * -1.0f, 0, 0 });
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mProjectionMatrix = projection;
|
mProjectionMatrix = projection;
|
||||||
|
@ -377,7 +381,7 @@ namespace Renderer
|
||||||
|
|
||||||
// This is required to avoid a brief white screen flash during startup on some systems.
|
// This is required to avoid a brief white screen flash during startup on some systems.
|
||||||
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
Renderer::drawRect(0.0f, 0.0f, static_cast<float>(Renderer::getScreenWidth()),
|
||||||
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
static_cast<float>(Renderer::getScreenHeight()), 0x000000FF, 0x000000FF);
|
||||||
swapBuffers();
|
swapBuffers();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -385,6 +389,7 @@ namespace Renderer
|
||||||
|
|
||||||
void deinit()
|
void deinit()
|
||||||
{
|
{
|
||||||
|
// Destroy the window.
|
||||||
destroyWindow();
|
destroyWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -400,32 +405,38 @@ namespace Renderer
|
||||||
switch (screenRotate) {
|
switch (screenRotate) {
|
||||||
case 0:
|
case 0:
|
||||||
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
|
box = Rect(screenOffsetX + box.x, screenOffsetY + box.y, box.w, box.h);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
box = Rect(windowWidth - screenOffsetY - box.y - box.h,
|
box = Rect(windowWidth - screenOffsetY - box.y - box.h, screenOffsetX + box.x,
|
||||||
screenOffsetX + box.x, box.h, box.w);
|
box.h, box.w);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
box = Rect(windowWidth - screenOffsetX - box.x - box.w, windowHeight -
|
box = Rect(windowWidth - screenOffsetX - box.x - box.w,
|
||||||
screenOffsetY - box.y - box.h, box.w, box.h);
|
windowHeight - screenOffsetY - box.y - box.h, box.w, box.h);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
box = Rect(screenOffsetY + box.y, windowHeight -
|
box = Rect(screenOffsetY + box.y, windowHeight - screenOffsetX - box.x - box.w,
|
||||||
screenOffsetX - box.x - box.w, box.h, box.w);
|
box.h, box.w);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the box fits within clipStack.top(), and clip further accordingly.
|
// Make sure the box fits within clipStack.top(), and clip further accordingly.
|
||||||
if (clipStack.size()) {
|
if (clipStack.size()) {
|
||||||
const Rect& top = clipStack.top();
|
const Rect& top = clipStack.top();
|
||||||
if ( top.x > box.x) box.x = top.x;
|
if (top.x > box.x)
|
||||||
if ( top.y > box.y) box.y = top.y;
|
box.x = top.x;
|
||||||
if ((top.x + top.w) < (box.x + box.w)) box.w = (top.x + top.w) - box.x;
|
if (top.y > box.y)
|
||||||
if ((top.y + top.h) < (box.y + box.h)) box.h = (top.y + top.h) - box.y;
|
box.y = top.y;
|
||||||
|
if ((top.x + top.w) < (box.x + box.w))
|
||||||
|
box.w = (top.x + top.w) - box.x;
|
||||||
|
if ((top.y + top.h) < (box.y + box.h))
|
||||||
|
box.h = (top.y + top.h) - box.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (box.w < 0) box.w = 0;
|
if (box.w < 0)
|
||||||
if (box.h < 0) box.h = 0;
|
box.w = 0;
|
||||||
|
if (box.h < 0)
|
||||||
|
box.h = 0;
|
||||||
|
|
||||||
clipStack.push(box);
|
clipStack.push(box);
|
||||||
|
|
||||||
|
@ -447,18 +458,17 @@ namespace Renderer
|
||||||
setScissor(clipStack.top());
|
setScissor(clipStack.top());
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawRect(
|
void drawRect(const float _x,
|
||||||
const float _x,
|
const float _y,
|
||||||
const float _y,
|
const float _w,
|
||||||
const float _w,
|
const float _h,
|
||||||
const float _h,
|
const unsigned int _color,
|
||||||
const unsigned int _color,
|
const unsigned int _colorEnd,
|
||||||
const unsigned int _colorEnd,
|
bool horizontalGradient,
|
||||||
bool horizontalGradient,
|
const float _opacity,
|
||||||
const float _opacity,
|
const Transform4x4f& _trans,
|
||||||
const Transform4x4f& _trans,
|
const Blend::Factor _srcBlendFactor,
|
||||||
const Blend::Factor _srcBlendFactor,
|
const Blend::Factor _dstBlendFactor)
|
||||||
const Blend::Factor _dstBlendFactor)
|
|
||||||
{
|
{
|
||||||
const unsigned int color = convertRGBAToABGR(_color);
|
const unsigned int color = convertRGBAToABGR(_color);
|
||||||
const unsigned int colorEnd = convertRGBAToABGR(_colorEnd);
|
const unsigned int colorEnd = convertRGBAToABGR(_colorEnd);
|
||||||
|
@ -474,10 +484,12 @@ namespace Renderer
|
||||||
if (_hL > 0.0f && _hL < 1.0f)
|
if (_hL > 0.0f && _hL < 1.0f)
|
||||||
_hL = 1.0f;
|
_hL = 1.0f;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
vertices[0] = { { _x , _y }, { 0.0f, 0.0f }, color };
|
vertices[0] = { { _x , _y }, { 0.0f, 0.0f }, color };
|
||||||
vertices[1] = { { _x , _y + _hL }, { 0.0f, 0.0f }, horizontalGradient ? colorEnd : color };
|
vertices[1] = { { _x , _y + _hL }, { 0.0f, 0.0f }, horizontalGradient ? colorEnd : color };
|
||||||
vertices[2] = { { _x + _wL, _y }, { 0.0f, 0.0f }, horizontalGradient ? color : colorEnd };
|
vertices[2] = { { _x + _wL, _y }, { 0.0f, 0.0f }, horizontalGradient ? color : colorEnd };
|
||||||
vertices[3] = { { _x + _wL, _y + _hL }, { 0.0f, 0.0f }, colorEnd };
|
vertices[3] = { { _x + _wL, _y + _hL }, { 0.0f, 0.0f }, colorEnd };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
// Round vertices.
|
// Round vertices.
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
|
@ -524,17 +536,13 @@ namespace Renderer
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sShaderProgramVector.size() > index-1)
|
if (sShaderProgramVector.size() > index - 1)
|
||||||
return sShaderProgramVector[index-1];
|
return sShaderProgramVector[index - 1];
|
||||||
else
|
else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
const Transform4x4f getProjectionMatrix()
|
const Transform4x4f getProjectionMatrix() { return mProjectionMatrix; }
|
||||||
{
|
|
||||||
return mProjectionMatrix;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_Window* getSDLWindow() { return sdlWindow; }
|
SDL_Window* getSDLWindow() { return sdlWindow; }
|
||||||
int getWindowWidth() { return windowWidth; }
|
int getWindowWidth() { return windowWidth; }
|
||||||
int getWindowHeight() { return windowHeight; }
|
int getWindowHeight() { return windowHeight; }
|
||||||
|
@ -547,4 +555,4 @@ namespace Renderer
|
||||||
float getScreenHeightModifier() { return screenHeightModifier; }
|
float getScreenHeightModifier() { return screenHeightModifier; }
|
||||||
float getScreenAspectRatio() { return screenAspectRatio; }
|
float getScreenAspectRatio() { return screenAspectRatio; }
|
||||||
|
|
||||||
} // Renderer::
|
} // namespace Renderer
|
||||||
|
|
|
@ -9,26 +9,27 @@
|
||||||
#ifndef ES_CORE_RENDERER_RENDERER_H
|
#ifndef ES_CORE_RENDERER_RENDERER_H
|
||||||
#define ES_CORE_RENDERER_RENDERER_H
|
#define ES_CORE_RENDERER_RENDERER_H
|
||||||
|
|
||||||
#include "math/Transform4x4f.h"
|
|
||||||
#include "math/Vector2f.h"
|
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Shader_GL21.h"
|
#include "Shader_GL21.h"
|
||||||
|
#include "math/Transform4x4f.h"
|
||||||
|
#include "math/Vector2f.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
class Transform4x4f;
|
|
||||||
class Vector2i;
|
|
||||||
struct SDL_Window;
|
struct SDL_Window;
|
||||||
|
|
||||||
|
class Transform4x4f;
|
||||||
|
class Vector2i;
|
||||||
|
|
||||||
namespace Renderer
|
namespace Renderer
|
||||||
{
|
{
|
||||||
const unsigned int SHADER_DESATURATE = 1;
|
const unsigned int SHADER_DESATURATE = 1;
|
||||||
const unsigned int SHADER_OPACITY = 2;
|
const unsigned int SHADER_OPACITY = 2;
|
||||||
const unsigned int SHADER_DIM = 4;
|
const unsigned int SHADER_DIM = 4;
|
||||||
const unsigned int SHADER_BLUR_HORIZONTAL = 8;
|
const unsigned int SHADER_BLUR_HORIZONTAL = 8;
|
||||||
const unsigned int SHADER_BLUR_VERTICAL = 16;
|
const unsigned int SHADER_BLUR_VERTICAL = 16;
|
||||||
const unsigned int SHADER_SCANLINES = 32;
|
const unsigned int SHADER_SCANLINES = 32;
|
||||||
|
|
||||||
struct shaderParameters {
|
struct shaderParameters {
|
||||||
std::array<GLfloat, 2> textureSize;
|
std::array<GLfloat, 2> textureSize;
|
||||||
|
@ -39,40 +40,40 @@ namespace Renderer
|
||||||
unsigned int blurPasses;
|
unsigned int blurPasses;
|
||||||
|
|
||||||
shaderParameters()
|
shaderParameters()
|
||||||
: textureSize({0.0f, 0.0f}),
|
: textureSize({ 0.0f, 0.0f })
|
||||||
textureCoordinates({0.0f, 0.0f, 0.0f, 0.0f}),
|
, textureCoordinates({ 0.0f, 0.0f, 0.0f, 0.0f })
|
||||||
fragmentSaturation(1.0f),
|
, fragmentSaturation(1.0f)
|
||||||
fragmentDimValue(0.4f),
|
, fragmentDimValue(0.4f)
|
||||||
fragmentOpacity(1.0f),
|
, fragmentOpacity(1.0f)
|
||||||
blurPasses(1)
|
, blurPasses(1)
|
||||||
{};
|
{
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::vector<Shader*> sShaderProgramVector;
|
static std::vector<Shader*> sShaderProgramVector;
|
||||||
static GLuint shaderFBO;
|
static GLuint shaderFBO;
|
||||||
static Transform4x4f mProjectionMatrix;
|
static Transform4x4f mProjectionMatrix;
|
||||||
|
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function))
|
#define GL_CHECK_ERROR(Function) (Function, _GLCheckError(#Function))
|
||||||
|
|
||||||
static void _GLCheckError(const std::string& _funcName)
|
static void _GLCheckError(const std::string& _funcName)
|
||||||
{
|
{
|
||||||
const GLenum errorCode = glGetError();
|
const GLenum errorCode = glGetError();
|
||||||
|
|
||||||
if (errorCode != GL_NO_ERROR) {
|
if (errorCode != GL_NO_ERROR) {
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
LOG(LogError) << "OpenGL error: " << _funcName <<
|
LOG(LogError) << "OpenGL error: " << _funcName << " failed with error code: 0x"
|
||||||
" failed with error code: 0x" << std::hex << errorCode;
|
<< std::hex << errorCode;
|
||||||
#else
|
#else
|
||||||
LOG(LogError) << "OpenGLES error: " << _funcName <<
|
LOG(LogError) << "OpenGLES error: " << _funcName << " failed with error code: 0x"
|
||||||
" failed with error code: 0x" << std::hex << errorCode;
|
<< std::hex << errorCode;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define GL_CHECK_ERROR(Function) (Function)
|
#define GL_CHECK_ERROR(Function) (Function)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace Blend
|
namespace Blend
|
||||||
{
|
{
|
||||||
|
@ -93,37 +94,33 @@ namespace Renderer
|
||||||
namespace Texture
|
namespace Texture
|
||||||
{
|
{
|
||||||
enum Type {
|
enum Type {
|
||||||
RGBA = 0,
|
RGBA = 0, // Replace with AllowShortEnumsOnASingleLine: false (clang-format >=11.0).
|
||||||
ALPHA = 1
|
ALPHA = 1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Rect {
|
struct Rect {
|
||||||
Rect(
|
Rect(const int _x, const int _y, const int _w, const int _h)
|
||||||
const int _x,
|
: x(_x)
|
||||||
const int _y,
|
, y(_y)
|
||||||
const int _w,
|
, w(_w)
|
||||||
const int _h)
|
, h(_h)
|
||||||
: x(_x),
|
{
|
||||||
y(_y),
|
}
|
||||||
w(_w),
|
|
||||||
h(_h) {}
|
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
int w;
|
int w;
|
||||||
int h;
|
int h;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Vertex
|
struct Vertex {
|
||||||
{
|
|
||||||
Vertex() {}
|
Vertex() {}
|
||||||
Vertex(
|
Vertex(const Vector2f& _pos, const Vector2f& _tex, const unsigned int _col)
|
||||||
const Vector2f& _pos,
|
: pos(_pos)
|
||||||
const Vector2f& _tex,
|
, tex(_tex)
|
||||||
const unsigned int _col)
|
, col(_col)
|
||||||
: pos(_pos),
|
{
|
||||||
tex(_tex),
|
}
|
||||||
col(_col) { }
|
|
||||||
Vector2f pos;
|
Vector2f pos;
|
||||||
Vector2f tex;
|
Vector2f tex;
|
||||||
unsigned int col;
|
unsigned int col;
|
||||||
|
@ -136,18 +133,17 @@ namespace Renderer
|
||||||
void deinit();
|
void deinit();
|
||||||
void pushClipRect(const Vector2i& _pos, const Vector2i& _size);
|
void pushClipRect(const Vector2i& _pos, const Vector2i& _size);
|
||||||
void popClipRect();
|
void popClipRect();
|
||||||
void drawRect(
|
void drawRect(const float _x,
|
||||||
const float _x,
|
const float _y,
|
||||||
const float _y,
|
const float _w,
|
||||||
const float _w,
|
const float _h,
|
||||||
const float _h,
|
const unsigned int _color,
|
||||||
const unsigned int _color,
|
const unsigned int _colorEnd,
|
||||||
const unsigned int _colorEnd,
|
bool horizontalGradient = false,
|
||||||
bool horizontalGradient = false,
|
const float _opacity = 1.0,
|
||||||
const float _opacity = 1.0,
|
const Transform4x4f& _trans = Transform4x4f::Identity(),
|
||||||
const Transform4x4f& _trans = Transform4x4f::Identity(),
|
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
||||||
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
|
||||||
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
|
|
||||||
SDL_Window* getSDLWindow();
|
SDL_Window* getSDLWindow();
|
||||||
int getWindowWidth();
|
int getWindowWidth();
|
||||||
int getWindowHeight();
|
int getWindowHeight();
|
||||||
|
@ -166,49 +162,46 @@ namespace Renderer
|
||||||
Shader* getShaderProgram(unsigned int shaderID);
|
Shader* getShaderProgram(unsigned int shaderID);
|
||||||
const Transform4x4f getProjectionMatrix();
|
const Transform4x4f getProjectionMatrix();
|
||||||
void shaderPostprocessing(unsigned int shaders,
|
void shaderPostprocessing(unsigned int shaders,
|
||||||
const Renderer::shaderParameters& parameters = shaderParameters(),
|
const Renderer::shaderParameters& parameters = shaderParameters(),
|
||||||
unsigned char* textureRGBA = nullptr);
|
unsigned char* textureRGBA = nullptr);
|
||||||
|
|
||||||
|
static unsigned int getWindowFlags() { return SDL_WINDOW_OPENGL; }
|
||||||
|
|
||||||
// API specific.
|
|
||||||
unsigned int getWindowFlags();
|
|
||||||
void setupWindow();
|
void setupWindow();
|
||||||
bool createContext();
|
bool createContext();
|
||||||
void destroyContext();
|
void destroyContext();
|
||||||
unsigned int createTexture(
|
unsigned int createTexture(const Texture::Type _type,
|
||||||
const Texture::Type _type,
|
const bool _linear,
|
||||||
const bool _linear,
|
const bool _repeat,
|
||||||
const bool _repeat,
|
const unsigned int _width,
|
||||||
const unsigned int _width,
|
const unsigned int _height,
|
||||||
const unsigned int _height,
|
void* _data);
|
||||||
void* _data);
|
|
||||||
void destroyTexture(const unsigned int _texture);
|
void destroyTexture(const unsigned int _texture);
|
||||||
void updateTexture(
|
void updateTexture(const unsigned int _texture,
|
||||||
const unsigned int _texture,
|
const Texture::Type _type,
|
||||||
const Texture::Type _type,
|
const unsigned int _x,
|
||||||
const unsigned int _x,
|
const unsigned _y,
|
||||||
const unsigned _y,
|
const unsigned int _width,
|
||||||
const unsigned int _width,
|
const unsigned int _height,
|
||||||
const unsigned int _height,
|
void* _data);
|
||||||
void* _data);
|
|
||||||
void bindTexture(const unsigned int _texture);
|
void bindTexture(const unsigned int _texture);
|
||||||
void drawLines(
|
void drawLines(const Vertex* _vertices,
|
||||||
const Vertex* _vertices,
|
const unsigned int _numVertices,
|
||||||
const unsigned int _numVertices,
|
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
||||||
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
|
||||||
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA);
|
void drawTriangleStrips(const Vertex* _vertices,
|
||||||
void drawTriangleStrips(
|
const unsigned int _numVertices,
|
||||||
const Vertex* _vertices,
|
const Transform4x4f& _trans = Transform4x4f::Identity(),
|
||||||
const unsigned int _numVertices,
|
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
||||||
const Transform4x4f& _trans = Transform4x4f::Identity(),
|
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA,
|
||||||
const Blend::Factor _srcBlendFactor = Blend::SRC_ALPHA,
|
const shaderParameters& _parameters = shaderParameters());
|
||||||
const Blend::Factor _dstBlendFactor = Blend::ONE_MINUS_SRC_ALPHA,
|
|
||||||
const shaderParameters& _parameters = shaderParameters());
|
|
||||||
void setProjection(const Transform4x4f& _projection);
|
void setProjection(const Transform4x4f& _projection);
|
||||||
void setMatrix(const Transform4x4f& _matrix);
|
void setMatrix(const Transform4x4f& _matrix);
|
||||||
void setViewport(const Rect& _viewport);
|
void setViewport(const Rect& _viewport);
|
||||||
void setScissor(const Rect& _scissor);
|
void setScissor(const Rect& _scissor);
|
||||||
void setSwapInterval();
|
void setSwapInterval();
|
||||||
void swapBuffers();
|
void swapBuffers();
|
||||||
}
|
|
||||||
|
} // namespace Renderer
|
||||||
|
|
||||||
#endif // ES_CORE_RENDERER_RENDERER_H
|
#endif // ES_CORE_RENDERER_RENDERER_H
|
||||||
|
|
|
@ -8,10 +8,10 @@
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
|
|
||||||
#include "math/Transform4x4f.h"
|
|
||||||
#include "renderers/Renderer.h"
|
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
#include "Shader_GL21.h"
|
#include "Shader_GL21.h"
|
||||||
|
#include "math/Transform4x4f.h"
|
||||||
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
namespace Renderer
|
namespace Renderer
|
||||||
{
|
{
|
||||||
|
@ -20,6 +20,7 @@ namespace Renderer
|
||||||
|
|
||||||
static GLenum convertBlendFactor(const Blend::Factor _blendFactor)
|
static GLenum convertBlendFactor(const Blend::Factor _blendFactor)
|
||||||
{
|
{
|
||||||
|
// clang-format off
|
||||||
switch (_blendFactor) {
|
switch (_blendFactor) {
|
||||||
case Blend::ZERO: { return GL_ZERO; } break;
|
case Blend::ZERO: { return GL_ZERO; } break;
|
||||||
case Blend::ONE: { return GL_ONE; } break;
|
case Blend::ONE: { return GL_ONE; } break;
|
||||||
|
@ -33,31 +34,29 @@ namespace Renderer
|
||||||
case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break;
|
case Blend::ONE_MINUS_DST_ALPHA: { return GL_ONE_MINUS_DST_ALPHA; } break;
|
||||||
default: { return GL_ZERO; }
|
default: { return GL_ZERO; }
|
||||||
}
|
}
|
||||||
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLenum convertTextureType(const Texture::Type _type)
|
static GLenum convertTextureType(const Texture::Type _type)
|
||||||
{
|
{
|
||||||
|
// clang-format off
|
||||||
switch (_type) {
|
switch (_type) {
|
||||||
case Texture::RGBA: { return GL_RGBA; } break;
|
case Texture::RGBA: { return GL_RGBA; } break;
|
||||||
case Texture::ALPHA: { return GL_ALPHA; } break;
|
case Texture::ALPHA: { return GL_ALPHA; } break;
|
||||||
default: { return GL_ZERO; }
|
default: { return GL_ZERO; }
|
||||||
}
|
}
|
||||||
}
|
// clang-format on
|
||||||
|
|
||||||
unsigned int getWindowFlags()
|
|
||||||
{
|
|
||||||
return SDL_WINDOW_OPENGL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupWindow()
|
void setupWindow()
|
||||||
{
|
{
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
// This is required on macOS, as the operating system will otherwise insist on using
|
// This is required on macOS, as the operating system will otherwise insist on using
|
||||||
// a newer OpenGL version which completely breaks the application.
|
// a newer OpenGL version which completely breaks the application.
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
|
||||||
#else
|
#else
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
#endif
|
#endif
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);
|
||||||
|
|
||||||
|
@ -78,32 +77,34 @@ namespace Renderer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
glewInit();
|
glewInit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
SDL_GL_MakeCurrent(getSDLWindow(), sdlContext);
|
||||||
|
|
||||||
std::string vendor = glGetString(GL_VENDOR) ?
|
std::string vendor =
|
||||||
reinterpret_cast<const char*>(glGetString(GL_VENDOR)) : "";
|
glGetString(GL_VENDOR) ? reinterpret_cast<const char*>(glGetString(GL_VENDOR)) : "";
|
||||||
std::string renderer = glGetString(GL_RENDERER) ?
|
std::string renderer =
|
||||||
reinterpret_cast<const char*>(glGetString(GL_RENDERER)) : "";
|
glGetString(GL_RENDERER) ? reinterpret_cast<const char*>(glGetString(GL_RENDERER)) : "";
|
||||||
std::string version = glGetString(GL_VERSION) ?
|
std::string version =
|
||||||
reinterpret_cast<const char*>(glGetString(GL_VERSION)) : "";
|
glGetString(GL_VERSION) ? reinterpret_cast<const char*>(glGetString(GL_VERSION)) : "";
|
||||||
std::string extensions = glGetString(GL_EXTENSIONS) ?
|
std::string extensions = glGetString(GL_EXTENSIONS) ?
|
||||||
reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)) : "";
|
reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)) :
|
||||||
|
"";
|
||||||
|
|
||||||
LOG(LogInfo) << "GL vendor: " << vendor;
|
LOG(LogInfo) << "GL vendor: " << vendor;
|
||||||
LOG(LogInfo) << "GL renderer: " << renderer;
|
LOG(LogInfo) << "GL renderer: " << renderer;
|
||||||
LOG(LogInfo) << "GL version: " << version;
|
LOG(LogInfo) << "GL version: " << version;
|
||||||
#if defined(_WIN64)
|
#if defined(_WIN64)
|
||||||
LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1 with GLEW";
|
LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1 with GLEW";
|
||||||
#else
|
#else
|
||||||
LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1";
|
LOG(LogInfo) << "EmulationStation renderer: OpenGL 2.1";
|
||||||
#endif
|
#endif
|
||||||
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
LOG(LogInfo) << "Checking available OpenGL extensions...";
|
||||||
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
std::string glExts = glGetString(GL_EXTENSIONS) ?
|
||||||
reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)) : "";
|
reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS)) :
|
||||||
|
"";
|
||||||
if (extensions.find("GL_ARB_texture_non_power_of_two") == std::string::npos) {
|
if (extensions.find("GL_ARB_texture_non_power_of_two") == std::string::npos) {
|
||||||
LOG(LogError) << "GL_ARB_texture_non_power_of_two: MISSING";
|
LOG(LogError) << "GL_ARB_texture_non_power_of_two: MISSING";
|
||||||
missingExtension = true;
|
missingExtension = true;
|
||||||
|
@ -137,7 +138,7 @@ namespace Renderer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data[4] = {255, 255, 255, 255};
|
uint8_t data[4] = { 255, 255, 255, 255 };
|
||||||
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data);
|
whiteTexture = createTexture(Texture::RGBA, false, true, 1, 1, data);
|
||||||
|
|
||||||
GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
|
GL_CHECK_ERROR(glClearColor(0.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
@ -162,13 +163,12 @@ namespace Renderer
|
||||||
sdlContext = nullptr;
|
sdlContext = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int createTexture(
|
unsigned int createTexture(const Texture::Type _type,
|
||||||
const Texture::Type _type,
|
const bool _linear,
|
||||||
const bool _linear,
|
const bool _repeat,
|
||||||
const bool _repeat,
|
const unsigned int _width,
|
||||||
const unsigned int _width,
|
const unsigned int _height,
|
||||||
const unsigned int _height,
|
void* _data)
|
||||||
void* _data)
|
|
||||||
{
|
{
|
||||||
const GLenum type = convertTextureType(_type);
|
const GLenum type = convertTextureType(_type);
|
||||||
unsigned int texture;
|
unsigned int texture;
|
||||||
|
@ -176,16 +176,19 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glGenTextures(1, &texture));
|
GL_CHECK_ERROR(glGenTextures(1, &texture));
|
||||||
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
|
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, texture));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, _repeat ?
|
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
|
||||||
static_cast<GLfloat>(GL_REPEAT) : static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
|
_repeat ? static_cast<GLfloat>(GL_REPEAT) :
|
||||||
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, _repeat ?
|
static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
|
||||||
static_cast<GLfloat>(GL_REPEAT) : static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
|
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
|
||||||
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, _linear ?
|
_repeat ? static_cast<GLfloat>(GL_REPEAT) :
|
||||||
static_cast<GLfloat>(GL_LINEAR) : static_cast<GLfloat>(GL_NEAREST)));
|
static_cast<GLfloat>(GL_CLAMP_TO_EDGE)));
|
||||||
|
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
|
||||||
|
_linear ? static_cast<GLfloat>(GL_LINEAR) :
|
||||||
|
static_cast<GLfloat>(GL_NEAREST)));
|
||||||
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
GL_CHECK_ERROR(glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type,
|
GL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, type, _width, _height, 0, type,
|
||||||
GL_UNSIGNED_BYTE, _data));
|
GL_UNSIGNED_BYTE, _data));
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
@ -195,20 +198,19 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glDeleteTextures(1, &_texture));
|
GL_CHECK_ERROR(glDeleteTextures(1, &_texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateTexture(
|
void updateTexture(const unsigned int _texture,
|
||||||
const unsigned int _texture,
|
const Texture::Type _type,
|
||||||
const Texture::Type _type,
|
const unsigned int _x,
|
||||||
const unsigned int _x,
|
const unsigned _y,
|
||||||
const unsigned _y,
|
const unsigned int _width,
|
||||||
const unsigned int _width,
|
const unsigned int _height,
|
||||||
const unsigned int _height,
|
void* _data)
|
||||||
void* _data)
|
|
||||||
{
|
{
|
||||||
const GLenum type = convertTextureType(_type);
|
const GLenum type = convertTextureType(_type);
|
||||||
|
|
||||||
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture));
|
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture));
|
||||||
GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, _width, _height,
|
GL_CHECK_ERROR(glTexSubImage2D(GL_TEXTURE_2D, 0, _x, _y, _width, _height, type,
|
||||||
type, GL_UNSIGNED_BYTE, _data));
|
GL_UNSIGNED_BYTE, _data));
|
||||||
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture));
|
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, whiteTexture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -220,29 +222,27 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture));
|
GL_CHECK_ERROR(glBindTexture(GL_TEXTURE_2D, _texture));
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawLines(
|
void drawLines(const Vertex* _vertices,
|
||||||
const Vertex* _vertices,
|
const unsigned int _numVertices,
|
||||||
const unsigned int _numVertices,
|
const Blend::Factor _srcBlendFactor,
|
||||||
const Blend::Factor _srcBlendFactor,
|
const Blend::Factor _dstBlendFactor)
|
||||||
const Blend::Factor _dstBlendFactor)
|
|
||||||
{
|
{
|
||||||
GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos));
|
GL_CHECK_ERROR(glVertexPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].pos));
|
||||||
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex));
|
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex));
|
||||||
GL_CHECK_ERROR(glColorPointer( 4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col));
|
GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor),
|
GL_CHECK_ERROR(
|
||||||
convertBlendFactor(_dstBlendFactor)));
|
glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor)));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_LINES, 0, _numVertices));
|
||||||
}
|
}
|
||||||
|
|
||||||
void drawTriangleStrips(
|
void drawTriangleStrips(const Vertex* _vertices,
|
||||||
const Vertex* _vertices,
|
const unsigned int _numVertices,
|
||||||
const unsigned int _numVertices,
|
const Transform4x4f& _trans,
|
||||||
const Transform4x4f& _trans,
|
const Blend::Factor _srcBlendFactor,
|
||||||
const Blend::Factor _srcBlendFactor,
|
const Blend::Factor _dstBlendFactor,
|
||||||
const Blend::Factor _dstBlendFactor,
|
const shaderParameters& _parameters)
|
||||||
const shaderParameters& _parameters)
|
|
||||||
{
|
{
|
||||||
float width = _vertices[3].pos[0];
|
float width = _vertices[3].pos[0];
|
||||||
float height = _vertices[3].pos[1];
|
float height = _vertices[3].pos[1];
|
||||||
|
@ -251,17 +251,17 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex));
|
GL_CHECK_ERROR(glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &_vertices[0].tex));
|
||||||
GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col));
|
GL_CHECK_ERROR(glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(Vertex), &_vertices[0].col));
|
||||||
|
|
||||||
GL_CHECK_ERROR(glBlendFunc(convertBlendFactor(_srcBlendFactor),
|
GL_CHECK_ERROR(
|
||||||
convertBlendFactor(_dstBlendFactor)));
|
glBlendFunc(convertBlendFactor(_srcBlendFactor), convertBlendFactor(_dstBlendFactor)));
|
||||||
|
|
||||||
#if defined(USE_OPENGL_21)
|
#if defined(USE_OPENGL_21)
|
||||||
if (_vertices[0].shaders == 0) {
|
if (_vertices[0].shaders == 0) {
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// If saturation is set below the maximum (default) value, run the
|
// If saturation is set below the maximum (default) value, run the
|
||||||
// desaturation shader.
|
// desaturation shader.
|
||||||
if (_vertices->saturation < 1.0 || _parameters.fragmentSaturation < 1.0) {
|
if (_vertices->saturation < 1.0f || _parameters.fragmentSaturation < 1.0f) {
|
||||||
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
Shader* runShader = getShaderProgram(SHADER_DESATURATE);
|
||||||
// Only try to use the shader if it has been loaded properly.
|
// Only try to use the shader if it has been loaded properly.
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
|
@ -278,9 +278,8 @@ namespace Renderer
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
_vertices->opacity < 1.0 ?
|
_vertices->opacity < 1.0f ? runShader->setOpacity(_vertices->opacity) :
|
||||||
runShader->setOpacity(_vertices->opacity) :
|
runShader->setOpacity(_parameters.fragmentOpacity);
|
||||||
runShader->setOpacity(_parameters.fragmentOpacity);
|
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
}
|
||||||
|
@ -303,7 +302,7 @@ namespace Renderer
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({width, height});
|
runShader->setTextureSize({ width, height });
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
}
|
||||||
|
@ -314,7 +313,7 @@ namespace Renderer
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({width, height});
|
runShader->setTextureSize({ width, height });
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
}
|
||||||
|
@ -338,20 +337,20 @@ namespace Renderer
|
||||||
// scanlines to videos with non-standard aspect ratios.
|
// scanlines to videos with non-standard aspect ratios.
|
||||||
float relativeWidth = width / getScreenWidth();
|
float relativeWidth = width / getScreenWidth();
|
||||||
float relativeAdjustment = (relativeWidth + relativeHeight) / 2.0f;
|
float relativeAdjustment = (relativeWidth + relativeHeight) / 2.0f;
|
||||||
float modifier = 1.41f + relativeAdjustment / 7.0f -
|
float modifier =
|
||||||
(0.14f * screenHeightModifier);
|
1.41f + relativeAdjustment / 7.0f - (0.14f * screenHeightModifier);
|
||||||
shaderHeight = height * modifier;
|
shaderHeight = height * modifier;
|
||||||
}
|
}
|
||||||
if (runShader) {
|
if (runShader) {
|
||||||
runShader->activateShaders();
|
runShader->activateShaders();
|
||||||
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
runShader->setModelViewProjectionMatrix(getProjectionMatrix() * _trans);
|
||||||
runShader->setTextureSize({shaderWidth, shaderHeight});
|
runShader->setTextureSize({ shaderWidth, shaderHeight });
|
||||||
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, _numVertices));
|
||||||
runShader->deactivateShaders();
|
runShader->deactivateShaders();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void setProjection(const Transform4x4f& _projection)
|
void setProjection(const Transform4x4f& _projection)
|
||||||
|
@ -372,8 +371,8 @@ namespace Renderer
|
||||||
void setViewport(const Rect& _viewport)
|
void setViewport(const Rect& _viewport)
|
||||||
{
|
{
|
||||||
// glViewport starts at the bottom left of the window.
|
// glViewport starts at the bottom left of the window.
|
||||||
GL_CHECK_ERROR(glViewport( _viewport.x, getWindowHeight() -
|
GL_CHECK_ERROR(glViewport(_viewport.x, getWindowHeight() - _viewport.y - _viewport.h,
|
||||||
_viewport.y - _viewport.h, _viewport.w, _viewport.h));
|
_viewport.w, _viewport.h));
|
||||||
}
|
}
|
||||||
|
|
||||||
void setScissor(const Rect& _scissor)
|
void setScissor(const Rect& _scissor)
|
||||||
|
@ -383,8 +382,8 @@ namespace Renderer
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// glScissor starts at the bottom left of the window.
|
// glScissor starts at the bottom left of the window.
|
||||||
GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() -
|
GL_CHECK_ERROR(glScissor(_scissor.x, getWindowHeight() - _scissor.y - _scissor.h,
|
||||||
_scissor.y - _scissor.h, _scissor.w, _scissor.h));
|
_scissor.w, _scissor.h));
|
||||||
GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST));
|
GL_CHECK_ERROR(glEnable(GL_SCISSOR_TEST));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -413,11 +412,12 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
GL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
|
||||||
}
|
}
|
||||||
|
|
||||||
void shaderPostprocessing(unsigned int shaders, const Renderer::shaderParameters& parameters,
|
void shaderPostprocessing(unsigned int shaders,
|
||||||
unsigned char* textureRGBA)
|
const Renderer::shaderParameters& parameters,
|
||||||
|
unsigned char* textureRGBA)
|
||||||
{
|
{
|
||||||
Vertex vertices[4];
|
Vertex vertices[4];
|
||||||
std::vector<unsigned int>shaderList;
|
std::vector<unsigned int> shaderList;
|
||||||
GLuint width = getScreenWidth();
|
GLuint width = getScreenWidth();
|
||||||
GLuint height = getScreenHeight();
|
GLuint height = getScreenHeight();
|
||||||
float widthf = static_cast<float>(width);
|
float widthf = static_cast<float>(width);
|
||||||
|
@ -425,10 +425,12 @@ namespace Renderer
|
||||||
|
|
||||||
// Set vertex positions and texture coordinates to full screen as all
|
// Set vertex positions and texture coordinates to full screen as all
|
||||||
// postprocessing is applied to the complete screen area.
|
// postprocessing is applied to the complete screen area.
|
||||||
vertices[0] = { { 0, 0 }, { 0, 1 }, 0 };
|
// clang-format off
|
||||||
vertices[1] = { { 0, heightf }, { 0, 0 }, 0 };
|
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 1.0f }, 0 };
|
||||||
vertices[2] = { { widthf, 0 }, { 1, 1 }, 0 };
|
vertices[1] = { { 0.0f , heightf }, { 0.0f, 0.0f }, 0 };
|
||||||
vertices[3] = { { widthf, heightf }, { 1, 0 }, 0};
|
vertices[2] = { { widthf, 0.0f }, { 1.0f, 1.0f }, 0 };
|
||||||
|
vertices[3] = { { widthf, heightf }, { 1.0f, 0.0f }, 0 };
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
if (shaders & Renderer::SHADER_DESATURATE)
|
if (shaders & Renderer::SHADER_DESATURATE)
|
||||||
shaderList.push_back(Renderer::SHADER_DESATURATE);
|
shaderList.push_back(Renderer::SHADER_DESATURATE);
|
||||||
|
@ -457,27 +459,24 @@ namespace Renderer
|
||||||
// For the blur shaders there is an optional variable to set the number of passes
|
// For the blur shaders there is an optional variable to set the number of passes
|
||||||
// to execute, which proportionally affects the blur amount.
|
// to execute, which proportionally affects the blur amount.
|
||||||
if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL ||
|
if (shaderList[i] == Renderer::SHADER_BLUR_HORIZONTAL ||
|
||||||
shaderList[i] == Renderer::SHADER_BLUR_VERTICAL)
|
shaderList[i] == Renderer::SHADER_BLUR_VERTICAL) {
|
||||||
shaderPasses = parameters.blurPasses;
|
shaderPasses = parameters.blurPasses;
|
||||||
|
}
|
||||||
|
|
||||||
for (int p = 0; p < shaderPasses; p++) {
|
for (int p = 0; p < shaderPasses; p++) {
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, shaderFBO));
|
||||||
|
|
||||||
// Attach the texture to the shader framebuffer.
|
// Attach the texture to the shader framebuffer.
|
||||||
GL_CHECK_ERROR(glFramebufferTexture2D(
|
GL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||||
GL_FRAMEBUFFER,
|
GL_TEXTURE_2D, screenTexture, 0));
|
||||||
GL_COLOR_ATTACHMENT0,
|
|
||||||
GL_TEXTURE_2D,
|
|
||||||
screenTexture,
|
|
||||||
0));
|
|
||||||
|
|
||||||
// Blit the screen contents to screenTexture.
|
// Blit the screen contents to screenTexture.
|
||||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||||
|
|
||||||
// Apply/render the shaders.
|
// Apply/render the shaders.
|
||||||
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(),
|
drawTriangleStrips(vertices, 4, Transform4x4f::Identity(), Blend::SRC_ALPHA,
|
||||||
Blend::SRC_ALPHA, Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
Blend::ONE_MINUS_SRC_ALPHA, parameters);
|
||||||
|
|
||||||
// If textureRGBA has an address, it means that the output should go to this
|
// If textureRGBA has an address, it means that the output should go to this
|
||||||
// texture rather than to the screen. The glReadPixels() function is slow, but
|
// texture rather than to the screen. The glReadPixels() function is slow, but
|
||||||
|
@ -485,8 +484,8 @@ namespace Renderer
|
||||||
// screen texture, it doesn't really matter.
|
// screen texture, it doesn't really matter.
|
||||||
if (textureRGBA) {
|
if (textureRGBA) {
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||||
GL_CHECK_ERROR(glReadPixels(0, 0, width, height,
|
GL_CHECK_ERROR(
|
||||||
GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
|
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, textureRGBA));
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -494,7 +493,7 @@ namespace Renderer
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_READ_FRAMEBUFFER, shaderFBO));
|
||||||
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
GL_CHECK_ERROR(glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0));
|
||||||
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
GL_CHECK_ERROR(glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
GL_COLOR_BUFFER_BIT, GL_NEAREST));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -503,6 +502,6 @@ namespace Renderer
|
||||||
destroyTexture(screenTexture);
|
destroyTexture(screenTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Renderer::
|
} // namespace Renderer
|
||||||
|
|
||||||
#endif // USE_OPENGL_21
|
#endif // USE_OPENGL_21
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue