Formatted the es-core source tree using clang-format.

This commit is contained in:
Leon Styhre 2021-07-07 20:31:46 +02:00
parent af5e32e121
commit 23fdc00044
121 changed files with 4362 additions and 4490 deletions

View file

@ -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;

View file

@ -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;
} }

View file

@ -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
} }
} }

View file

@ -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;

View file

@ -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++)

View file

@ -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;

View file

@ -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");

View file

@ -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.

View file

@ -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.

View file

@ -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);

View file

@ -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);
}; };

View file

@ -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);

View file

@ -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;

View file

@ -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());

View file

@ -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();

View file

@ -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;
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
}
} }
} }

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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")));

View file

@ -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 = "");

View file

@ -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);
} }

View file

@ -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;
}; };

View file

@ -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;
} }

View file

@ -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();

View file

@ -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;
}; };

View file

@ -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)
{ {
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;
} }

View file

@ -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();
} }

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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);
} }
} }

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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"))

View file

@ -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;

View file

@ -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");

View file

@ -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();

View file

@ -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);
} }
} }

View file

@ -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);

View file

@ -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;
} }

View file

@ -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();

View file

@ -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")));
} }
} }

View file

@ -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;

View file

@ -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

View file

@ -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());

View file

@ -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();

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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()

View file

@ -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();

View file

@ -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");

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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()

View file

@ -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;

View file

@ -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 {

View file

@ -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);

View file

@ -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();

View file

@ -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 {

View file

@ -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;

View file

@ -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();
} }

View file

@ -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)

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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.

View file

@ -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;

View file

@ -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;
} }

View file

@ -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();

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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;

View file

@ -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 }; }

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -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

View file

@ -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