mirror of
https://github.com/RetroDECK/ES-DE.git
synced 2024-11-22 06:05:38 +00:00
Code cleanup and code documentation update.
As of this commit, the initial code cleanup and code documentation has been completed for the entire application.
This commit is contained in:
parent
5b17edde8b
commit
e4fdd1e20d
|
@ -25,7 +25,7 @@ Some key points:
|
||||||
* Name member variables starting with a small 'm', e.g. mMyMemberVariable
|
* Name member variables starting with a small 'm', e.g. mMyMemberVariable
|
||||||
* Use the same naming convention for functions as for local variables, e.g. someFunction()
|
* Use the same naming convention for functions as for local variables, e.g. someFunction()
|
||||||
* Inline functions makes perfect sense to use, but don't overdo it by using them for functions that won't be called very frequently
|
* Inline functions makes perfect sense to use, but don't overdo it by using them for functions that won't be called very frequently
|
||||||
* Never put more than one statement on a single line, except for lambda expressions
|
* Never put more than one statement on a single line (there are some exceptions though like lambda expressions and some switch statements)
|
||||||
* Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone
|
* Avoid overoptimizations, especially if it sacrifices readability, makes the code hard to expand on or is error prone
|
||||||
* For the rest, check the code and have fun! :)
|
* For the rest, check the code and have fun! :)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// EmulationStation.h
|
// EmulationStation.h
|
||||||
//
|
//
|
||||||
// Version and build information.
|
// Version and build information.
|
||||||
//
|
//
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SystemScreenSaver.cpp
|
// SystemScreenSaver.cpp
|
||||||
//
|
//
|
||||||
// Screensaver, supporting the following modes:
|
// Screensaver, supporting the following modes:
|
||||||
// Dim, black, video, slideshow.
|
// Dim, black, video, slideshow.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "SystemScreenSaver.h"
|
#include "SystemScreenSaver.h"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// SystemScreenSaver.h
|
// SystemScreenSaver.h
|
||||||
//
|
//
|
||||||
// Screensaver, supporting the following modes:
|
// Screensaver, supporting the following modes:
|
||||||
// Dim, black, video, slideshow.
|
// Dim, black, video, slideshow.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// GuiCollectionSystemsOptions.cpp
|
// GuiCollectionSystemsOptions.cpp
|
||||||
//
|
//
|
||||||
// User interface for the game collection settings.
|
// User interface for the game collection settings.
|
||||||
// Submenu to the GuiMenu main menu.
|
// Submenu to the GuiMenu main menu.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "guis/GuiCollectionSystemsOptions.h"
|
#include "guis/GuiCollectionSystemsOptions.h"
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// GuiCollectionSystemsOptions.h
|
// GuiCollectionSystemsOptions.h
|
||||||
//
|
//
|
||||||
// User interface for the game collection settings.
|
// User interface for the game collection settings.
|
||||||
// Submenu to the GuiMenu main menu.
|
// Submenu to the GuiMenu main menu.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//
|
//
|
||||||
// User interface for the screensaver options.
|
// User interface for the screensaver options.
|
||||||
// Based on the GuiScreenSaverOptions template.
|
// Based on the GuiScreenSaverOptions template.
|
||||||
// Submenu to the GuiMenu main menu.
|
// Submenu to the GuiMenu main menu.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// GuiSlideshowScreensaverOptions.h
|
// GuiSlideshowScreensaverOptions.h
|
||||||
//
|
//
|
||||||
// User interface for the slideshow screensaver options.
|
// User interface for the slideshow screensaver options.
|
||||||
// Submenu to GuiGeneralScreensaverOptions.
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
|
|
@ -59,7 +59,7 @@ GuiVideoScreensaverOptions::GuiVideoScreensaverOptions(Window* window, const cha
|
||||||
|
|
||||||
// Set subtitle position.
|
// Set subtitle position.
|
||||||
auto ss_omx_subs_align = std::make_shared<OptionListComponent<std::string>>
|
auto ss_omx_subs_align = std::make_shared<OptionListComponent<std::string>>
|
||||||
(mWindow, "GAME INFO ALIGNMENT", false);
|
(mWindow, getHelpStyle(), "GAME INFO ALIGNMENT", false);
|
||||||
std::vector<std::string> align_mode;
|
std::vector<std::string> align_mode;
|
||||||
align_mode.push_back("left");
|
align_mode.push_back("left");
|
||||||
align_mode.push_back("center");
|
align_mode.push_back("center");
|
||||||
|
@ -132,7 +132,7 @@ void GuiVideoScreensaverOptions::save()
|
||||||
"never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
"never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
|
||||||
if (startingStatusNotRisky && endStatusRisky) {
|
if (startingStatusNotRisky && endStatusRisky) {
|
||||||
// If before it wasn't risky but now there's a risk of problems, show warning.
|
// If before it wasn't risky but now there's a risk of problems, show warning.
|
||||||
mWindow->pushGui(new GuiMsgBox(mWindow,
|
mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||||
"Using OMX Player and displaying Game Info may result in the video flickering in "
|
"Using OMX Player and displaying Game Info may result in the video flickering in "
|
||||||
"some TV modes. If that happens, consider:\n\n• Disabling the \"Show Game Info\" "
|
"some TV modes. If that happens, consider:\n\n• Disabling the \"Show Game Info\" "
|
||||||
"option;\n• Disabling \"Overscan\" on the Pi configuration menu might help:\nRetroPie > "
|
"option;\n• Disabling \"Overscan\" on the Pi configuration menu might help:\nRetroPie > "
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//
|
//
|
||||||
// GuiVideoScreensaverOptions.h
|
// GuiVideoScreensaverOptions.h
|
||||||
//
|
//
|
||||||
// User interface for the video screensaver options.
|
// User interface for the video screensaver options.
|
||||||
// Submenu to GuiGeneralScreensaverOptions.
|
// Submenu to GuiGeneralScreensaverOptions.
|
||||||
|
|
|
@ -24,9 +24,9 @@
|
||||||
// able to have it throw in case of error with the following:
|
// able to have it throw in case of error with the following:
|
||||||
//ifndef RAPIDJSON_ASSERT
|
//ifndef RAPIDJSON_ASSERT
|
||||||
//#define RAPIDJSON_ASSERT(x) \
|
//#define RAPIDJSON_ASSERT(x) \
|
||||||
// if (!(x)) { \
|
// if (!(x)) { \
|
||||||
// throw std::runtime_error("rapidjson internal assertion failure: " #x); \
|
// throw std::runtime_error("rapidjson internal assertion failure: " #x); \
|
||||||
// }
|
// }
|
||||||
//#endif // RAPIDJSON_ASSERT
|
//#endif // RAPIDJSON_ASSERT
|
||||||
|
|
||||||
#include <rapidjson/document.h>
|
#include <rapidjson/document.h>
|
||||||
|
|
|
@ -44,11 +44,11 @@ bool HttpReq::isUrl(const std::string& str)
|
||||||
std::string::npos || str.find("www.") != std::string::npos));
|
std::string::npos || str.find("www.") != std::string::npos));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(NULL)
|
HttpReq::HttpReq(const std::string& url) : mStatus(REQ_IN_PROGRESS), mHandle(nullptr)
|
||||||
{
|
{
|
||||||
mHandle = curl_easy_init();
|
mHandle = curl_easy_init();
|
||||||
|
|
||||||
if (mHandle == NULL) {
|
if (mHandle == nullptr) {
|
||||||
mStatus = REQ_IO_ERROR;
|
mStatus = REQ_IO_ERROR;
|
||||||
onError("curl_easy_init failed");
|
onError("curl_easy_init failed");
|
||||||
return;
|
return;
|
||||||
|
@ -147,7 +147,7 @@ HttpReq::Status HttpReq::status()
|
||||||
if (msg->msg == CURLMSG_DONE) {
|
if (msg->msg == CURLMSG_DONE) {
|
||||||
HttpReq* req = s_requests[msg->easy_handle];
|
HttpReq* req = s_requests[msg->easy_handle];
|
||||||
|
|
||||||
if (req == NULL) {
|
if (req == nullptr) {
|
||||||
LOG(LogError) << "Cannot find easy handle!";
|
LOG(LogError) << "Cannot find easy handle!";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ void touch(const std::string& filename)
|
||||||
{
|
{
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
FILE* fp = fopen(filename.c_str(), "ab+");
|
FILE* fp = fopen(filename.c_str(), "ab+");
|
||||||
if (fp != NULL)
|
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);
|
||||||
|
|
|
@ -11,11 +11,12 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Scripting.h"
|
#include "Scripting.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
|
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
Settings* Settings::sInstance = NULL;
|
Settings* Settings::sInstance = nullptr;
|
||||||
|
|
||||||
// These values are NOT saved to es_settings.cfg since they're not set via
|
// These values are NOT saved to es_settings.cfg since they're not set via
|
||||||
// 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,
|
||||||
|
@ -59,7 +60,7 @@ Settings::Settings()
|
||||||
|
|
||||||
Settings* Settings::getInstance()
|
Settings* Settings::getInstance()
|
||||||
{
|
{
|
||||||
if (sInstance == NULL)
|
if (sInstance == nullptr)
|
||||||
sInstance = new Settings();
|
sInstance = new Settings();
|
||||||
|
|
||||||
return sInstance;
|
return sInstance;
|
||||||
|
|
|
@ -82,7 +82,7 @@ bool NavigationSounds::isPlayingThemeNavigationSound(NavigationSoundsID soundID)
|
||||||
|
|
||||||
Sound::Sound(
|
Sound::Sound(
|
||||||
const std::string & path)
|
const std::string & path)
|
||||||
: mSampleData(NULL),
|
: mSampleData(nullptr),
|
||||||
mSamplePos(0),
|
mSamplePos(0),
|
||||||
mSampleLength(0),
|
mSampleLength(0),
|
||||||
playing(false)
|
playing(false)
|
||||||
|
@ -103,7 +103,7 @@ void Sound::loadFile(const std::string & path)
|
||||||
|
|
||||||
void Sound::init()
|
void Sound::init()
|
||||||
{
|
{
|
||||||
if(mSampleData != NULL)
|
if(mSampleData != nullptr)
|
||||||
deinit();
|
deinit();
|
||||||
|
|
||||||
if(mPath.empty())
|
if(mPath.empty())
|
||||||
|
|
|
@ -1,86 +1,90 @@
|
||||||
|
//
|
||||||
|
// AnimatedImageComponent.cpp
|
||||||
|
//
|
||||||
|
// Creates animation from multiple images files.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/AnimatedImageComponent.h"
|
#include "components/AnimatedImageComponent.h"
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
|
|
||||||
AnimatedImageComponent::AnimatedImageComponent(Window* window) : GuiComponent(window), mEnabled(false)
|
AnimatedImageComponent::AnimatedImageComponent(Window* window)
|
||||||
|
: GuiComponent(window), mEnabled(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::load(const AnimationDef* def)
|
void AnimatedImageComponent::load(const AnimationDef* def)
|
||||||
{
|
{
|
||||||
mFrames.clear();
|
mFrames.clear();
|
||||||
|
|
||||||
assert(def->frameCount >= 1);
|
assert(def->frameCount >= 1);
|
||||||
|
|
||||||
for(size_t i = 0; i < def->frameCount; i++)
|
for (size_t i = 0; i < def->frameCount; i++) {
|
||||||
{
|
if (def->frames[i].path != nullptr &&
|
||||||
if(def->frames[i].path != NULL && !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;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto img = std::unique_ptr<ImageComponent>(new ImageComponent(mWindow));
|
auto img = std::unique_ptr<ImageComponent>(new ImageComponent(mWindow));
|
||||||
img->setResize(mSize.x(), mSize.y());
|
img->setResize(mSize.x(), mSize.y());
|
||||||
img->setImage(std::string(def->frames[i].path), false);
|
img->setImage(std::string(def->frames[i].path), false);
|
||||||
|
|
||||||
mFrames.push_back(ImageFrame(std::move(img), def->frames[i].time));
|
mFrames.push_back(ImageFrame(std::move(img), def->frames[i].time));
|
||||||
}
|
}
|
||||||
|
|
||||||
mLoop = def->loop;
|
mLoop = def->loop;
|
||||||
|
|
||||||
mCurrentFrame = 0;
|
mCurrentFrame = 0;
|
||||||
mFrameAccumulator = 0;
|
mFrameAccumulator = 0;
|
||||||
mEnabled = true;
|
mEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::reset()
|
void AnimatedImageComponent::reset()
|
||||||
{
|
{
|
||||||
mCurrentFrame = 0;
|
mCurrentFrame = 0;
|
||||||
mFrameAccumulator = 0;
|
mFrameAccumulator = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::onSizeChanged()
|
void AnimatedImageComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
for(auto it = mFrames.cbegin(); it != mFrames.cend(); it++)
|
for (auto it = mFrames.cbegin(); it != mFrames.cend(); it++) {
|
||||||
{
|
it->first->setResize(mSize.x(), mSize.y());
|
||||||
it->first->setResize(mSize.x(), mSize.y());
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::update(int deltaTime)
|
void AnimatedImageComponent::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if(!mEnabled || mFrames.size() == 0)
|
if (!mEnabled || mFrames.size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mFrameAccumulator += deltaTime;
|
mFrameAccumulator += deltaTime;
|
||||||
|
|
||||||
while(mFrames.at(mCurrentFrame).second <= mFrameAccumulator)
|
while (mFrames.at(mCurrentFrame).second <= mFrameAccumulator) {
|
||||||
{
|
mCurrentFrame++;
|
||||||
mCurrentFrame++;
|
|
||||||
|
|
||||||
if(mCurrentFrame == (int)mFrames.size())
|
if (mCurrentFrame == (int)mFrames.size()) {
|
||||||
{
|
if (mLoop) {
|
||||||
if(mLoop)
|
// Restart.
|
||||||
{
|
mCurrentFrame = 0;
|
||||||
// restart
|
}
|
||||||
mCurrentFrame = 0;
|
else {
|
||||||
}else{
|
// Done, stop at last frame.
|
||||||
// done, stop at last frame
|
mCurrentFrame--;
|
||||||
mCurrentFrame--;
|
mEnabled = false;
|
||||||
mEnabled = false;
|
break;
|
||||||
break;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mFrameAccumulator -= mFrames.at(mCurrentFrame).second;
|
mFrameAccumulator -= mFrames.at(mCurrentFrame).second;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AnimatedImageComponent::render(const Transform4x4f& trans)
|
void AnimatedImageComponent::render(const Transform4x4f& trans)
|
||||||
{
|
{
|
||||||
if(mFrames.size())
|
if (mFrames.size())
|
||||||
mFrames.at(mCurrentFrame).first->render(getTransform() * trans);
|
mFrames.at(mCurrentFrame).first->render(getTransform() * trans);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// AnimatedImageComponent.h
|
||||||
|
//
|
||||||
|
// Creates animation from multiple images files.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
#define ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
||||||
|
@ -6,42 +12,40 @@
|
||||||
|
|
||||||
class ImageComponent;
|
class ImageComponent;
|
||||||
|
|
||||||
struct AnimationFrame
|
struct AnimationFrame {
|
||||||
{
|
const char* path;
|
||||||
const char* path;
|
int time;
|
||||||
int time;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AnimationDef
|
struct AnimationDef {
|
||||||
{
|
AnimationFrame* frames;
|
||||||
AnimationFrame* frames;
|
size_t frameCount;
|
||||||
size_t frameCount;
|
bool loop;
|
||||||
bool loop;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AnimatedImageComponent : public GuiComponent
|
class AnimatedImageComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AnimatedImageComponent(Window* window);
|
AnimatedImageComponent(Window* window);
|
||||||
|
|
||||||
void load(const AnimationDef* def); // no reference to def is kept after loading is complete
|
void load(const AnimationDef* def); // No reference to def is kept after loading is complete.
|
||||||
|
|
||||||
void reset(); // set to frame 0
|
void reset(); // Set to frame 0.
|
||||||
|
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
void render(const Transform4x4f& trans) override;
|
void render(const Transform4x4f& trans) override;
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
typedef std::pair<std::unique_ptr<ImageComponent>, int> ImageFrame;
|
typedef std::pair<std::unique_ptr<ImageComponent>, int> ImageFrame;
|
||||||
|
|
||||||
std::vector<ImageFrame> mFrames;
|
std::vector<ImageFrame> mFrames;
|
||||||
|
|
||||||
bool mLoop;
|
bool mLoop;
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
int mFrameAccumulator;
|
int mFrameAccumulator;
|
||||||
int mCurrentFrame;
|
int mCurrentFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
|
||||||
|
|
|
@ -1,56 +1,65 @@
|
||||||
|
//
|
||||||
|
// BusyComponent.cpp
|
||||||
|
//
|
||||||
|
// Animated busy indicator.
|
||||||
|
//
|
||||||
|
|
||||||
#include "BusyComponent.h"
|
#include "BusyComponent.h"
|
||||||
|
|
||||||
#include "components/AnimatedImageComponent.h"
|
#include "components/AnimatedImageComponent.h"
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
|
|
||||||
// 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): GuiComponent(window),
|
||||||
mBackground(window, ":/graphics/frame.png"), mGrid(window, Vector2i(5, 3))
|
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...", Font::get(FONT_SIZE_MEDIUM), 0x777777FF);
|
mText = std::make_shared<TextComponent>(mWindow, "WORKING...",
|
||||||
|
Font::get(FONT_SIZE_MEDIUM), 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);
|
||||||
mGrid.setEntry(mText, Vector2i(3, 1), false, true);
|
mGrid.setEntry(mText, Vector2i(3, 1), false, true);
|
||||||
|
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&mGrid);
|
addChild(&mGrid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusyComponent::onSizeChanged()
|
void BusyComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mGrid.setSize(mSize);
|
mGrid.setSize(mSize);
|
||||||
|
|
||||||
if(mSize.x() == 0 || mSize.y() == 0)
|
if (mSize.x() == 0 || mSize.y() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const float middleSpacerWidth = 0.01f * Renderer::getScreenWidth();
|
const float middleSpacerWidth = 0.01f * Renderer::getScreenWidth();
|
||||||
const float textHeight = mText->getFont()->getLetterHeight();
|
const float textHeight = mText->getFont()->getLetterHeight();
|
||||||
mText->setSize(0, textHeight);
|
mText->setSize(0, textHeight);
|
||||||
const float textWidth = mText->getSize().x() + 4;
|
const float textWidth = mText->getSize().x() + 4;
|
||||||
|
|
||||||
mGrid.setColWidthPerc(1, textHeight / mSize.x()); // animation is square
|
mGrid.setColWidthPerc(1, textHeight / mSize.x()); // Animation is square.
|
||||||
mGrid.setColWidthPerc(2, middleSpacerWidth / mSize.x());
|
mGrid.setColWidthPerc(2, middleSpacerWidth / mSize.x());
|
||||||
mGrid.setColWidthPerc(3, textWidth / mSize.x());
|
mGrid.setColWidthPerc(3, textWidth / mSize.x());
|
||||||
|
|
||||||
mGrid.setRowHeightPerc(1, textHeight / mSize.y());
|
mGrid.setRowHeightPerc(1, textHeight / mSize.y());
|
||||||
|
|
||||||
mBackground.fitTo(Vector2f(mGrid.getColWidth(1) + mGrid.getColWidth(2) + mGrid.getColWidth(3), textHeight + 2),
|
mBackground.fitTo(Vector2f(mGrid.getColWidth(1) +
|
||||||
mAnimation->getPosition(), Vector2f(0, 0));
|
mGrid.getColWidth(2) + mGrid.getColWidth(3), textHeight + 2),
|
||||||
|
mAnimation->getPosition(), Vector2f(0, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
void BusyComponent::reset()
|
void BusyComponent::reset()
|
||||||
{
|
{
|
||||||
//mAnimation->reset();
|
//mAnimation->reset();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// BusyComponent.h
|
||||||
|
//
|
||||||
|
// Animated busy indicator.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -12,18 +18,18 @@ class TextComponent;
|
||||||
class BusyComponent : public GuiComponent
|
class BusyComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BusyComponent(Window* window);
|
BusyComponent(Window* window);
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
void reset(); // reset to frame 0
|
void reset(); // Reset to frame 0.
|
||||||
|
|
||||||
private:
|
private:
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
ComponentGrid mGrid;
|
ComponentGrid mGrid;
|
||||||
|
|
||||||
std::shared_ptr<AnimatedImageComponent> mAnimation;
|
std::shared_ptr<AnimatedImageComponent> mAnimation;
|
||||||
std::shared_ptr<TextComponent> mText;
|
std::shared_ptr<TextComponent> mText;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_BUSY_COMPONENT_H
|
||||||
|
|
|
@ -1,119 +1,128 @@
|
||||||
|
//
|
||||||
|
// ButtonComponent.cpp
|
||||||
|
//
|
||||||
|
// Basic on/off button.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/ButtonComponent.h"
|
#include "components/ButtonComponent.h"
|
||||||
|
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
|
|
||||||
ButtonComponent::ButtonComponent(Window* window, const std::string& text, const std::string& helpText, const std::function<void()>& func) : GuiComponent(window),
|
ButtonComponent::ButtonComponent(
|
||||||
mBox(window, ":/graphics/button.png"),
|
Window* window, const std::string& text,
|
||||||
mFont(Font::get(FONT_SIZE_MEDIUM)),
|
const std::string& helpText,
|
||||||
mFocused(false),
|
const std::function<void()>& func)
|
||||||
mEnabled(true),
|
: GuiComponent(window),
|
||||||
mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF)
|
mBox(window, ":/graphics/button.png"),
|
||||||
|
mFont(Font::get(FONT_SIZE_MEDIUM)),
|
||||||
|
mFocused(false),
|
||||||
|
mEnabled(true),
|
||||||
|
mTextColorFocused(0xFFFFFFFF), mTextColorUnfocused(0x777777FF)
|
||||||
{
|
{
|
||||||
setPressedFunc(func);
|
setPressedFunc(func);
|
||||||
setText(text, helpText);
|
setText(text, helpText);
|
||||||
updateImage();
|
updateImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::onSizeChanged()
|
void ButtonComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::setPressedFunc(std::function<void()> f)
|
void ButtonComponent::setPressedFunc(std::function<void()> f)
|
||||||
{
|
{
|
||||||
mPressedFunc = f;
|
mPressedFunc = f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ButtonComponent::input(InputConfig* config, Input input)
|
bool ButtonComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if(config->isMappedTo("a", input) && input.value != 0)
|
if (config->isMappedTo("a", input) && input.value != 0) {
|
||||||
{
|
if (mPressedFunc && mEnabled)
|
||||||
if(mPressedFunc && mEnabled)
|
mPressedFunc();
|
||||||
mPressedFunc();
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::setText(const std::string& text, const std::string& helpText)
|
void ButtonComponent::setText(const std::string& text, const std::string& helpText)
|
||||||
{
|
{
|
||||||
mText = Utils::String::toUpper(text);
|
mText = Utils::String::toUpper(text);
|
||||||
mHelpText = helpText;
|
mHelpText = helpText;
|
||||||
|
|
||||||
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;
|
float minWidth = mFont->sizeText("DELETE").x() + 12;
|
||||||
setSize(Math::max(mTextCache->metrics.size.x() + 12, minWidth), mTextCache->metrics.size.y());
|
setSize(Math::max(mTextCache->metrics.size.x() + 12, minWidth), mTextCache->metrics.size.y());
|
||||||
|
|
||||||
updateHelpPrompts();
|
updateHelpPrompts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::onFocusGained()
|
void ButtonComponent::onFocusGained()
|
||||||
{
|
{
|
||||||
mFocused = true;
|
mFocused = true;
|
||||||
updateImage();
|
updateImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::onFocusLost()
|
void ButtonComponent::onFocusLost()
|
||||||
{
|
{
|
||||||
mFocused = false;
|
mFocused = false;
|
||||||
updateImage();
|
updateImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::setEnabled(bool enabled)
|
void ButtonComponent::setEnabled(bool enabled)
|
||||||
{
|
{
|
||||||
mEnabled = enabled;
|
mEnabled = enabled;
|
||||||
updateImage();
|
updateImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::updateImage()
|
void ButtonComponent::updateImage()
|
||||||
{
|
{
|
||||||
if(!mEnabled || !mPressedFunc)
|
if (!mEnabled || !mPressedFunc) {
|
||||||
{
|
mBox.setImagePath(":/graphics/button_filled.png");
|
||||||
mBox.setImagePath(":/graphics/button_filled.png");
|
mBox.setCenterColor(0x770000FF);
|
||||||
mBox.setCenterColor(0x770000FF);
|
mBox.setEdgeColor(0x770000FF);
|
||||||
mBox.setEdgeColor(0x770000FF);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mBox.setCenterColor(0xFFFFFFFF);
|
mBox.setCenterColor(0xFFFFFFFF);
|
||||||
mBox.setEdgeColor(0xFFFFFFFF);
|
mBox.setEdgeColor(0xFFFFFFFF);
|
||||||
mBox.setImagePath(mFocused ? ":/graphics/button_filled.png" : ":/graphics/button.png");
|
mBox.setImagePath(mFocused ? ":/graphics/button_filled.png" : ":/graphics/button.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ButtonComponent::render(const Transform4x4f& parentTrans)
|
void ButtonComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
mBox.render(trans);
|
mBox.render(trans);
|
||||||
|
|
||||||
if(mTextCache)
|
if (mTextCache)
|
||||||
{
|
{
|
||||||
Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2, (mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
|
Vector3f centerOffset((mSize.x() - mTextCache->metrics.size.x()) / 2,
|
||||||
trans = trans.translate(centerOffset);
|
(mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
|
||||||
|
trans = trans.translate(centerOffset);
|
||||||
|
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
mTextCache->setColor(getCurTextColor());
|
mTextCache->setColor(getCurTextColor());
|
||||||
mFont->renderTextCache(mTextCache.get());
|
mFont->renderTextCache(mTextCache.get());
|
||||||
trans = trans.translate(-centerOffset);
|
trans = trans.translate(-centerOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChildren(trans);
|
renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int ButtonComponent::getCurTextColor() const
|
unsigned int ButtonComponent::getCurTextColor() const
|
||||||
{
|
{
|
||||||
if(!mFocused)
|
if (!mFocused)
|
||||||
return mTextColorUnfocused;
|
return mTextColorUnfocused;
|
||||||
else
|
else
|
||||||
return mTextColorFocused;
|
return mTextColorFocused;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> ButtonComponent::getHelpPrompts()
|
std::vector<HelpPrompt> ButtonComponent::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts;
|
std::vector<HelpPrompt> prompts;
|
||||||
prompts.push_back(HelpPrompt("a", mHelpText.empty() ? mText.c_str() : mHelpText.c_str()));
|
prompts.push_back(HelpPrompt("a", mHelpText.empty() ? mText.c_str() : mHelpText.c_str()));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// ButtonComponent.h
|
||||||
|
//
|
||||||
|
// Basic on/off button.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -10,42 +16,42 @@ class TextCache;
|
||||||
class ButtonComponent : public GuiComponent
|
class ButtonComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ButtonComponent(Window* window, const std::string& text = "", const std::string& helpText = "", const std::function<void()>& func = nullptr);
|
ButtonComponent(Window* window, 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);
|
||||||
|
void setEnabled(bool enable);
|
||||||
|
|
||||||
void setEnabled(bool enable);
|
bool input(InputConfig* config, Input input) override;
|
||||||
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override;
|
void setText(const std::string& text, const std::string& helpText);
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
|
||||||
|
|
||||||
void setText(const std::string& text, const std::string& helpText);
|
inline const std::string& getText() const { return mText; };
|
||||||
|
inline const std::function<void()>& getPressedFunc() const { return mPressedFunc; };
|
||||||
|
|
||||||
inline const std::string& getText() const { return mText; };
|
void onSizeChanged() override;
|
||||||
inline const std::function<void()>& getPressedFunc() const { return mPressedFunc; };
|
void onFocusGained() override;
|
||||||
|
void onFocusLost() override;
|
||||||
|
|
||||||
void onSizeChanged() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
void onFocusGained() override;
|
|
||||||
void onFocusLost() override;
|
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<Font> mFont;
|
std::shared_ptr<Font> mFont;
|
||||||
std::function<void()> mPressedFunc;
|
std::function<void()> mPressedFunc;
|
||||||
|
|
||||||
bool mFocused;
|
bool mFocused;
|
||||||
bool mEnabled;
|
bool mEnabled;
|
||||||
unsigned int mTextColorFocused;
|
unsigned int mTextColorFocused;
|
||||||
unsigned int mTextColorUnfocused;
|
unsigned int mTextColorUnfocused;
|
||||||
|
|
||||||
unsigned int getCurTextColor() const;
|
unsigned int getCurTextColor() const;
|
||||||
void updateImage();
|
void updateImage();
|
||||||
|
|
||||||
std::string mText;
|
std::string mText;
|
||||||
std::string mHelpText;
|
std::string mHelpText;
|
||||||
std::unique_ptr<TextCache> mTextCache;
|
std::unique_ptr<TextCache> mTextCache;
|
||||||
NinePatchComponent mBox;
|
NinePatchComponent mBox;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
|
||||||
|
|
|
@ -100,7 +100,7 @@ void ComponentGrid::setEntry(
|
||||||
{
|
{
|
||||||
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);
|
||||||
assert(comp->getParent() == NULL);
|
assert(comp->getParent() == nullptr);
|
||||||
|
|
||||||
GridEntry entry(pos, size, comp, canFocus, resize, updateType, border);
|
GridEntry entry(pos, size, comp, canFocus, resize, updateType, border);
|
||||||
mCells.push_back(entry);
|
mCells.push_back(entry);
|
||||||
|
@ -223,7 +223,7 @@ const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
|
||||||
return &(*it);
|
return &(*it);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NULL;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ComponentGrid::input(InputConfig* config, Input input)
|
bool ComponentGrid::input(InputConfig* config, Input input)
|
||||||
|
@ -327,7 +327,7 @@ void ComponentGrid::onFocusGained()
|
||||||
bool ComponentGrid::cursorValid()
|
bool ComponentGrid::cursorValid()
|
||||||
{
|
{
|
||||||
const GridEntry* e = getCellAt(mCursor);
|
const GridEntry* e = getCellAt(mCursor);
|
||||||
return (e != NULL && e->canFocus);
|
return (e != nullptr && e->canFocus);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ComponentGrid::update(int deltaTime)
|
void ComponentGrid::update(int deltaTime)
|
||||||
|
@ -359,7 +359,7 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
|
||||||
void ComponentGrid::textInput(const char* text)
|
void ComponentGrid::textInput(const char* text)
|
||||||
{
|
{
|
||||||
const GridEntry* selectedEntry = getCellAt(mCursor);
|
const GridEntry* selectedEntry = getCellAt(mCursor);
|
||||||
if (selectedEntry != NULL && selectedEntry->canFocus)
|
if (selectedEntry != nullptr && selectedEntry->canFocus)
|
||||||
selectedEntry->component->textInput(text);
|
selectedEntry->component->textInput(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,7 @@ private:
|
||||||
|
|
||||||
operator bool() const
|
operator bool() const
|
||||||
{
|
{
|
||||||
return component != NULL;
|
return component != nullptr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
|
||||||
{
|
{
|
||||||
IList<ComponentListRow, void*>::Entry e;
|
IList<ComponentListRow, void*>::Entry e;
|
||||||
e.name = "";
|
e.name = "";
|
||||||
e.object = NULL;
|
e.object = nullptr;
|
||||||
e.data = row;
|
e.data = row;
|
||||||
|
|
||||||
this->add(e);
|
this->add(e);
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GridTileComponent.cpp
|
||||||
|
//
|
||||||
|
// X*Y grid.
|
||||||
|
//
|
||||||
|
|
||||||
#include "GridTileComponent.h"
|
#include "GridTileComponent.h"
|
||||||
|
|
||||||
#include "animations/LambdaAnimation.h"
|
#include "animations/LambdaAnimation.h"
|
||||||
|
@ -6,318 +12,310 @@
|
||||||
|
|
||||||
GridTileComponent::GridTileComponent(Window* window) : GuiComponent(window), mBackground(window)
|
GridTileComponent::GridTileComponent(Window* window) : GuiComponent(window), mBackground(window)
|
||||||
{
|
{
|
||||||
mDefaultProperties.mSize = getDefaultTileSize();
|
mDefaultProperties.mSize = getDefaultTileSize();
|
||||||
mDefaultProperties.mPadding = Vector2f(16.0f, 16.0f);
|
mDefaultProperties.mPadding = Vector2f(16.0f, 16.0f);
|
||||||
mDefaultProperties.mImageColor = 0xAAAAAABB;
|
mDefaultProperties.mImageColor = 0xAAAAAABB;
|
||||||
mDefaultProperties.mBackgroundImage = ":/graphics/frame.png";
|
mDefaultProperties.mBackgroundImage = ":/graphics/frame.png";
|
||||||
mDefaultProperties.mBackgroundCornerSize = Vector2f(16 ,16);
|
mDefaultProperties.mBackgroundCornerSize = Vector2f(16 ,16);
|
||||||
mDefaultProperties.mBackgroundCenterColor = 0xAAAAEEFF;
|
mDefaultProperties.mBackgroundCenterColor = 0xAAAAEEFF;
|
||||||
mDefaultProperties.mBackgroundEdgeColor = 0xAAAAEEFF;
|
mDefaultProperties.mBackgroundEdgeColor = 0xAAAAEEFF;
|
||||||
|
|
||||||
mSelectedProperties.mSize = getSelectedTileSize();
|
mSelectedProperties.mSize = getSelectedTileSize();
|
||||||
mSelectedProperties.mPadding = mDefaultProperties.mPadding;
|
mSelectedProperties.mPadding = mDefaultProperties.mPadding;
|
||||||
mSelectedProperties.mImageColor = 0xFFFFFFFF;
|
mSelectedProperties.mImageColor = 0xFFFFFFFF;
|
||||||
mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
|
mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
|
||||||
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
|
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
|
||||||
mSelectedProperties.mBackgroundCenterColor = 0xFFFFFFFF;
|
mSelectedProperties.mBackgroundCenterColor = 0xFFFFFFFF;
|
||||||
mSelectedProperties.mBackgroundEdgeColor = 0xFFFFFFFF;
|
mSelectedProperties.mBackgroundEdgeColor = 0xFFFFFFFF;
|
||||||
|
|
||||||
mImage = std::make_shared<ImageComponent>(mWindow);
|
mImage = std::make_shared<ImageComponent>(mWindow);
|
||||||
mImage->setOrigin(0.5f, 0.5f);
|
mImage->setOrigin(0.5f, 0.5f);
|
||||||
|
|
||||||
mBackground.setOrigin(0.5f, 0.5f);
|
mBackground.setOrigin(0.5f, 0.5f);
|
||||||
|
|
||||||
addChild(&mBackground);
|
addChild(&mBackground);
|
||||||
addChild(&(*mImage));
|
addChild(&(*mImage));
|
||||||
|
|
||||||
mSelectedZoomPercent = 0;
|
mSelectedZoomPercent = 0;
|
||||||
|
|
||||||
setSelected(false, false);
|
setSelected(false, false);
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::render(const Transform4x4f& parentTrans)
|
void GridTileComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
Transform4x4f trans = getTransform() * parentTrans;
|
Transform4x4f trans = getTransform() * parentTrans;
|
||||||
|
|
||||||
if (mVisible)
|
if (mVisible)
|
||||||
renderChildren(trans);
|
renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update all the tile properties to the new status (selected or default)
|
// Update all the tile properties to the new status (selected or default).
|
||||||
void GridTileComponent::update(int deltaTime)
|
void GridTileComponent::update(int deltaTime)
|
||||||
{
|
{
|
||||||
GuiComponent::update(deltaTime);
|
GuiComponent::update(deltaTime);
|
||||||
|
|
||||||
calcCurrentProperties();
|
calcCurrentProperties();
|
||||||
|
|
||||||
mBackground.setImagePath(mCurrentProperties.mBackgroundImage);
|
mBackground.setImagePath(mCurrentProperties.mBackgroundImage);
|
||||||
|
|
||||||
mImage->setColorShift(mCurrentProperties.mImageColor);
|
mImage->setColorShift(mCurrentProperties.mImageColor);
|
||||||
mBackground.setCenterColor(mCurrentProperties.mBackgroundCenterColor);
|
mBackground.setCenterColor(mCurrentProperties.mBackgroundCenterColor);
|
||||||
mBackground.setEdgeColor(mCurrentProperties.mBackgroundEdgeColor);
|
mBackground.setEdgeColor(mCurrentProperties.mBackgroundEdgeColor);
|
||||||
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties)
|
void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTileProperties* properties)
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(),
|
||||||
|
(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;
|
||||||
|
|
||||||
if (elem->has("padding"))
|
if (elem->has("padding"))
|
||||||
properties->mPadding = elem->get<Vector2f>("padding");
|
properties->mPadding = elem->get<Vector2f>("padding");
|
||||||
|
|
||||||
if (elem->has("imageColor"))
|
if (elem->has("imageColor"))
|
||||||
properties->mImageColor = elem->get<unsigned int>("imageColor");
|
properties->mImageColor = elem->get<unsigned int>("imageColor");
|
||||||
|
|
||||||
if (elem->has("backgroundImage"))
|
if (elem->has("backgroundImage"))
|
||||||
properties->mBackgroundImage = elem->get<std::string>("backgroundImage");
|
properties->mBackgroundImage = elem->get<std::string>("backgroundImage");
|
||||||
|
|
||||||
if (elem->has("backgroundCornerSize"))
|
if (elem->has("backgroundCornerSize"))
|
||||||
properties->mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
|
properties->mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
|
||||||
|
|
||||||
if (elem->has("backgroundColor"))
|
if (elem->has("backgroundColor")) {
|
||||||
{
|
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
|
||||||
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
|
properties->mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
|
||||||
properties->mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (elem->has("backgroundCenterColor"))
|
if (elem->has("backgroundCenterColor"))
|
||||||
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundCenterColor");
|
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundCenterColor");
|
||||||
|
|
||||||
if (elem->has("backgroundEdgeColor"))
|
if (elem->has("backgroundEdgeColor"))
|
||||||
properties->mBackgroundEdgeColor = elem->get<unsigned int>("backgroundEdgeColor");
|
properties->mBackgroundEdgeColor = elem->get<unsigned int>("backgroundEdgeColor");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& /*element*/, unsigned int /*properties*/)
|
void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
|
||||||
|
const std::string& view, const std::string& /*element*/, unsigned int /*properties*/)
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(),
|
||||||
|
(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");
|
||||||
if (elem)
|
if (elem)
|
||||||
applyThemeToProperties(elem, &mDefaultProperties);
|
applyThemeToProperties(elem, &mDefaultProperties);
|
||||||
|
|
||||||
// Apply theme to the selected gridtile
|
// Apply theme to the selected gridtile. Note that some of the default gridtile
|
||||||
// NOTE that some of the default gridtile properties influence on the selected gridtile properties
|
// properties have influence on the selected gridtile properties.
|
||||||
// See THEMES.md for more informations
|
// See THEMES.md for more informations.
|
||||||
elem = theme->getElement(view, "selected", "gridtile");
|
elem = theme->getElement(view, "selected", "gridtile");
|
||||||
|
|
||||||
mSelectedProperties.mSize = getSelectedTileSize();
|
mSelectedProperties.mSize = getSelectedTileSize();
|
||||||
mSelectedProperties.mPadding = mDefaultProperties.mPadding;
|
mSelectedProperties.mPadding = mDefaultProperties.mPadding;
|
||||||
mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
|
mSelectedProperties.mBackgroundImage = mDefaultProperties.mBackgroundImage;
|
||||||
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
|
mSelectedProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize;
|
||||||
|
|
||||||
if (elem)
|
if (elem)
|
||||||
applyThemeToProperties(elem, &mSelectedProperties);
|
applyThemeToProperties(elem, &mSelectedProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Made this a static function because the ImageGridComponent need to know the default tile size
|
// Made this a static function because the ImageGridComponent needs to know the default tile
|
||||||
// to calculate the grid dimension before it instantiate the GridTileComponents
|
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
|
||||||
Vector2f GridTileComponent::getDefaultTileSize()
|
Vector2f GridTileComponent::getDefaultTileSize()
|
||||||
{
|
{
|
||||||
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(),
|
||||||
|
(float)Renderer::getScreenHeight());
|
||||||
|
|
||||||
return screen * 0.22f;
|
return screen * 0.22f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f GridTileComponent::getSelectedTileSize() const
|
Vector2f GridTileComponent::getSelectedTileSize() const
|
||||||
{
|
{
|
||||||
return mDefaultProperties.mSize * 1.2f;
|
return mDefaultProperties.mSize * 1.2f;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GridTileComponent::isSelected() const
|
bool GridTileComponent::isSelected() const
|
||||||
{
|
{
|
||||||
return mSelected;
|
return mSelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::reset()
|
void GridTileComponent::reset()
|
||||||
{
|
{
|
||||||
setImage("");
|
setImage("");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setImage(const std::string& path)
|
void GridTileComponent::setImage(const std::string& path)
|
||||||
{
|
{
|
||||||
mImage->setImage(path);
|
mImage->setImage(path);
|
||||||
|
|
||||||
// Resize now to prevent flickering images when scrolling
|
// Resize now to prevent flickering images when scrolling.
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture)
|
void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture)
|
||||||
{
|
{
|
||||||
mImage->setImage(texture);
|
mImage->setImage(texture);
|
||||||
|
|
||||||
// Resize now to prevent flickering images when scrolling
|
// Resize now to prevent flickering images when scrolling.
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setSelected(bool selected, bool allowAnimation, Vector3f* pPosition, bool force)
|
void GridTileComponent::setSelected(bool selected, bool allowAnimation,
|
||||||
|
Vector3f* pPosition, bool force)
|
||||||
{
|
{
|
||||||
if (mSelected == selected && !force)
|
if (mSelected == selected && !force)
|
||||||
{
|
return;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSelected = selected;
|
mSelected = selected;
|
||||||
|
|
||||||
if (selected)
|
if (selected) {
|
||||||
{
|
if (pPosition == nullptr || !allowAnimation) {
|
||||||
if (pPosition == NULL || !allowAnimation)
|
cancelAnimation(3);
|
||||||
{
|
|
||||||
cancelAnimation(3);
|
|
||||||
|
|
||||||
this->setSelectedZoom(1);
|
this->setSelectedZoom(1);
|
||||||
mAnimPosition = Vector3f(0, 0, 0);
|
mAnimPosition = Vector3f(0, 0, 0);
|
||||||
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
mAnimPosition = Vector3f(pPosition->x(), pPosition->y(), pPosition->z());
|
||||||
mAnimPosition = Vector3f(pPosition->x(), pPosition->y(), pPosition->z());
|
|
||||||
|
|
||||||
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);
|
||||||
};
|
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
||||||
|
this->setSelectedZoom(1);
|
||||||
|
mAnimPosition = Vector3f(0, 0, 0);
|
||||||
|
}, false, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If (!selected).
|
||||||
|
else {
|
||||||
|
if (!allowAnimation) {
|
||||||
|
cancelAnimation(3);
|
||||||
|
this->setSelectedZoom(0);
|
||||||
|
|
||||||
cancelAnimation(3);
|
resize();
|
||||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
}
|
||||||
this->setSelectedZoom(1);
|
else {
|
||||||
mAnimPosition = Vector3f(0, 0, 0);
|
this->setSelectedZoom(1);
|
||||||
}, false, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else // if (!selected)
|
|
||||||
{
|
|
||||||
if (!allowAnimation)
|
|
||||||
{
|
|
||||||
cancelAnimation(3);
|
|
||||||
this->setSelectedZoom(0);
|
|
||||||
|
|
||||||
resize();
|
auto func = [this](float t) {
|
||||||
}
|
t -= 1; // Cubic ease out.
|
||||||
else
|
float pct = Math::lerp(0, 1, t*t*t + 1);
|
||||||
{
|
this->setSelectedZoom(1.0 - pct);
|
||||||
this->setSelectedZoom(1);
|
};
|
||||||
|
|
||||||
auto func = [this](float t)
|
cancelAnimation(3);
|
||||||
{
|
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
||||||
t -= 1; // cubic ease out
|
this->setSelectedZoom(0);
|
||||||
float pct = Math::lerp(0, 1, t*t*t + 1);
|
}, false, 3);
|
||||||
this->setSelectedZoom(1.0 - pct);
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
cancelAnimation(3);
|
|
||||||
setAnimation(new LambdaAnimation(func, 250), 0, [this] {
|
|
||||||
this->setSelectedZoom(0);
|
|
||||||
}, false, 3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setSelectedZoom(float percent)
|
void GridTileComponent::setSelectedZoom(float percent)
|
||||||
{
|
{
|
||||||
if (mSelectedZoomPercent == percent)
|
if (mSelectedZoomPercent == percent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mSelectedZoomPercent = percent;
|
mSelectedZoomPercent = percent;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::setVisible(bool visible)
|
void GridTileComponent::setVisible(bool visible)
|
||||||
{
|
{
|
||||||
mVisible = visible;
|
mVisible = visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::resize()
|
void GridTileComponent::resize()
|
||||||
{
|
{
|
||||||
calcCurrentProperties();
|
calcCurrentProperties();
|
||||||
|
|
||||||
mImage->setMaxSize(mCurrentProperties.mSize - mCurrentProperties.mPadding * 2);
|
mImage->setMaxSize(mCurrentProperties.mSize - mCurrentProperties.mPadding * 2);
|
||||||
mBackground.setCornerSize(mCurrentProperties.mBackgroundCornerSize);
|
mBackground.setCornerSize(mCurrentProperties.mBackgroundCornerSize);
|
||||||
mBackground.fitTo(mCurrentProperties.mSize - mBackground.getCornerSize() * 2);
|
mBackground.fitTo(mCurrentProperties.mSize - mBackground.getCornerSize() * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int mixColors(unsigned int first, unsigned int second, float percent)
|
unsigned int mixColors(unsigned int first, unsigned int second, float percent)
|
||||||
{
|
{
|
||||||
unsigned char alpha0 = (first >> 24) & 0xFF;
|
unsigned char alpha0 = (first >> 24) & 0xFF;
|
||||||
unsigned char blue0 = (first >> 16) & 0xFF;
|
unsigned char blue0 = (first >> 16) & 0xFF;
|
||||||
unsigned char green0 = (first >> 8) & 0xFF;
|
unsigned char green0 = (first >> 8) & 0xFF;
|
||||||
unsigned char red0 = first & 0xFF;
|
unsigned char red0 = first & 0xFF;
|
||||||
|
|
||||||
unsigned char alpha1 = (second >> 24) & 0xFF;
|
unsigned char alpha1 = (second >> 24) & 0xFF;
|
||||||
unsigned char blue1 = (second >> 16) & 0xFF;
|
unsigned char blue1 = (second >> 16) & 0xFF;
|
||||||
unsigned char green1 = (second >> 8) & 0xFF;
|
unsigned char green1 = (second >> 8) & 0xFF;
|
||||||
unsigned char red1 = second & 0xFF;
|
unsigned char red1 = second & 0xFF;
|
||||||
|
|
||||||
unsigned char alpha = (unsigned char)(alpha0 * (1.0 - percent) + alpha1 * percent);
|
unsigned char alpha = (unsigned char)(alpha0 * (1.0 - percent) + alpha1 * percent);
|
||||||
unsigned char blue = (unsigned char)(blue0 * (1.0 - percent) + blue1 * percent);
|
unsigned char blue = (unsigned char)(blue0 * (1.0 - percent) + blue1 * percent);
|
||||||
unsigned char green = (unsigned char)(green0 * (1.0 - percent) + green1 * percent);
|
unsigned char green = (unsigned char)(green0 * (1.0 - percent) + green1 * percent);
|
||||||
unsigned char red = (unsigned char)(red0 * (1.0 - percent) + red1 * percent);
|
unsigned char red = (unsigned char)(red0 * (1.0 - percent) + red1 * percent);
|
||||||
|
|
||||||
return (alpha << 24) | (blue << 16) | (green << 8) | red;
|
return (alpha << 24) | (blue << 16) | (green << 8) | red;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GridTileComponent::calcCurrentProperties()
|
void GridTileComponent::calcCurrentProperties()
|
||||||
{
|
{
|
||||||
mCurrentProperties = mSelected ? mSelectedProperties : mDefaultProperties;
|
mCurrentProperties = mSelected ? mSelectedProperties : mDefaultProperties;
|
||||||
|
|
||||||
float zoomPercentInverse = 1.0 - mSelectedZoomPercent;
|
float zoomPercentInverse = 1.0 - mSelectedZoomPercent;
|
||||||
|
|
||||||
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 + mSelectedProperties.mSize * mSelectedZoomPercent;
|
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse +
|
||||||
}
|
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 = mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor, mSelectedZoomPercent);
|
mSelectedProperties.mImageColor, mSelectedZoomPercent);
|
||||||
}
|
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
|
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
|
||||||
{
|
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize *
|
||||||
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent;
|
zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize *
|
||||||
}
|
mSelectedZoomPercent;
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
|
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
|
||||||
{
|
mCurrentProperties.mBackgroundCenterColor =
|
||||||
mCurrentProperties.mBackgroundCenterColor = mixColors(mDefaultProperties.mBackgroundCenterColor, mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
|
mixColors(mDefaultProperties.mBackgroundCenterColor,
|
||||||
}
|
mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
|
||||||
|
|
||||||
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
|
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
|
||||||
{
|
mCurrentProperties.mBackgroundEdgeColor =
|
||||||
mCurrentProperties.mBackgroundEdgeColor = mixColors(mDefaultProperties.mBackgroundEdgeColor, mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
|
mixColors(mDefaultProperties.mBackgroundEdgeColor,
|
||||||
}
|
mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector3f GridTileComponent::getBackgroundPosition()
|
Vector3f GridTileComponent::getBackgroundPosition()
|
||||||
{
|
{
|
||||||
return mBackground.getPosition() + mPosition;
|
return mBackground.getPosition() + mPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> GridTileComponent::getTexture()
|
std::shared_ptr<TextureResource> GridTileComponent::getTexture()
|
||||||
{
|
{
|
||||||
if (mImage != nullptr)
|
if (mImage != nullptr)
|
||||||
return mImage->getTexture();
|
return mImage->getTexture();
|
||||||
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
void GridTileComponent::forceSize(Vector2f size, float selectedZoom)
|
void GridTileComponent::forceSize(Vector2f size, float selectedZoom)
|
||||||
{
|
{
|
||||||
mDefaultProperties.mSize = size;
|
mDefaultProperties.mSize = size;
|
||||||
mSelectedProperties.mSize = size * selectedZoom;
|
mSelectedProperties.mSize = size * selectedZoom;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// GridTileComponent.h
|
||||||
|
//
|
||||||
|
// X*Y grid.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
#define ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
||||||
|
@ -5,63 +11,64 @@
|
||||||
#include "NinePatchComponent.h"
|
#include "NinePatchComponent.h"
|
||||||
#include "ImageComponent.h"
|
#include "ImageComponent.h"
|
||||||
|
|
||||||
struct GridTileProperties
|
struct GridTileProperties {
|
||||||
{
|
Vector2f mSize;
|
||||||
Vector2f mSize;
|
Vector2f mPadding;
|
||||||
Vector2f mPadding;
|
unsigned int mImageColor;
|
||||||
unsigned int mImageColor;
|
std::string mBackgroundImage;
|
||||||
std::string mBackgroundImage;
|
Vector2f mBackgroundCornerSize;
|
||||||
Vector2f mBackgroundCornerSize;
|
unsigned int mBackgroundCenterColor;
|
||||||
unsigned int mBackgroundCenterColor;
|
unsigned int mBackgroundEdgeColor;
|
||||||
unsigned int mBackgroundEdgeColor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class GridTileComponent : public GuiComponent
|
class GridTileComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
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, const std::string& element, unsigned int properties) override;
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
||||||
|
const std::string& element, unsigned int properties) override;
|
||||||
|
|
||||||
// Made this a static function because the ImageGridComponent need to know the default tile max size
|
// Made this a static function because the ImageGridComponent needs to know the default tile
|
||||||
// to calculate the grid dimension before it instantiate the GridTileComponents
|
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
|
||||||
static Vector2f getDefaultTileSize();
|
static Vector2f getDefaultTileSize();
|
||||||
Vector2f getSelectedTileSize() const;
|
Vector2f getSelectedTileSize() const;
|
||||||
bool isSelected() const;
|
bool isSelected() const;
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
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, Vector3f* pPosition = NULL, bool force=false);
|
void setSelected(bool selected, bool allowAnimation = true,
|
||||||
void setVisible(bool visible);
|
Vector3f* pPosition = nullptr, bool force=false);
|
||||||
|
void setVisible(bool visible);
|
||||||
|
|
||||||
void forceSize(Vector2f size, float selectedZoom);
|
void forceSize(Vector2f size, float selectedZoom);
|
||||||
|
|
||||||
Vector3f getBackgroundPosition();
|
Vector3f getBackgroundPosition();
|
||||||
|
|
||||||
virtual void update(int deltaTime) override;
|
virtual void update(int deltaTime) override;
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> getTexture();
|
std::shared_ptr<TextureResource> getTexture();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void resize();
|
void resize();
|
||||||
void calcCurrentProperties();
|
void calcCurrentProperties();
|
||||||
void setSelectedZoom(float percent);
|
void setSelectedZoom(float percent);
|
||||||
|
|
||||||
std::shared_ptr<ImageComponent> mImage;
|
std::shared_ptr<ImageComponent> mImage;
|
||||||
NinePatchComponent mBackground;
|
NinePatchComponent mBackground;
|
||||||
|
|
||||||
GridTileProperties mDefaultProperties;
|
GridTileProperties mDefaultProperties;
|
||||||
GridTileProperties mSelectedProperties;
|
GridTileProperties mSelectedProperties;
|
||||||
GridTileProperties mCurrentProperties;
|
GridTileProperties mCurrentProperties;
|
||||||
|
|
||||||
float mSelectedZoomPercent;
|
float mSelectedZoomPercent;
|
||||||
bool mSelected;
|
bool mSelected;
|
||||||
bool mVisible;
|
bool mVisible;
|
||||||
|
|
||||||
Vector3f mAnimPosition;
|
Vector3f mAnimPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// HelpComponent.cpp
|
||||||
|
//
|
||||||
|
// Help information in icon and text pairs.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/HelpComponent.h"
|
#include "components/HelpComponent.h"
|
||||||
|
|
||||||
#include "components/ComponentGrid.h"
|
#include "components/ComponentGrid.h"
|
||||||
|
@ -8,25 +14,25 @@
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#define OFFSET_X 12 // move the entire thing right by this amount (px)
|
#define OFFSET_X 12 // Move the entire thing right by this amount (px).
|
||||||
#define OFFSET_Y 12 // move the entire thing up by this amount (px)
|
#define OFFSET_Y 12 // Move the entire thing up by this amount (px).
|
||||||
|
|
||||||
#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 const std::map<std::string, const char*> ICON_PATH_MAP {
|
static const std::map<std::string, const char*> ICON_PATH_MAP {
|
||||||
{ "up/down", ":/help/dpad_updown.svg" },
|
{ "up/down", ":/help/dpad_updown.svg" },
|
||||||
{ "left/right", ":/help/dpad_leftright.svg" },
|
{ "left/right", ":/help/dpad_leftright.svg" },
|
||||||
{ "up/down/left/right", ":/help/dpad_all.svg" },
|
{ "up/down/left/right", ":/help/dpad_all.svg" },
|
||||||
{ "a", ":/help/button_a.svg" },
|
{ "a", ":/help/button_a.svg" },
|
||||||
{ "b", ":/help/button_b.svg" },
|
{ "b", ":/help/button_b.svg" },
|
||||||
{ "x", ":/help/button_x.svg" },
|
{ "x", ":/help/button_x.svg" },
|
||||||
{ "y", ":/help/button_y.svg" },
|
{ "y", ":/help/button_y.svg" },
|
||||||
{ "l", ":/help/button_l.svg" },
|
{ "l", ":/help/button_l.svg" },
|
||||||
{ "r", ":/help/button_r.svg" },
|
{ "r", ":/help/button_r.svg" },
|
||||||
{ "lr", ":/help/button_lr.svg" },
|
{ "lr", ":/help/button_lr.svg" },
|
||||||
{ "start", ":/help/button_start.svg" },
|
{ "start", ":/help/button_start.svg" },
|
||||||
{ "select", ":/help/button_select.svg" }
|
{ "select", ":/help/button_select.svg" }
|
||||||
};
|
};
|
||||||
|
|
||||||
HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
|
HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
|
||||||
|
@ -35,108 +41,106 @@ HelpComponent::HelpComponent(Window* window) : GuiComponent(window)
|
||||||
|
|
||||||
void HelpComponent::clearPrompts()
|
void HelpComponent::clearPrompts()
|
||||||
{
|
{
|
||||||
mPrompts.clear();
|
mPrompts.clear();
|
||||||
updateGrid();
|
updateGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HelpComponent::setPrompts(const std::vector<HelpPrompt>& prompts)
|
void HelpComponent::setPrompts(const std::vector<HelpPrompt>& prompts)
|
||||||
{
|
{
|
||||||
mPrompts = prompts;
|
mPrompts = prompts;
|
||||||
updateGrid();
|
updateGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HelpComponent::setStyle(const HelpStyle& style)
|
void HelpComponent::setStyle(const HelpStyle& style)
|
||||||
{
|
{
|
||||||
mStyle = style;
|
mStyle = style;
|
||||||
updateGrid();
|
updateGrid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HelpComponent::updateGrid()
|
void HelpComponent::updateGrid()
|
||||||
{
|
{
|
||||||
if(!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty())
|
if (!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty()) {
|
||||||
{
|
mGrid.reset();
|
||||||
mGrid.reset();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<Font>& font = mStyle.font;
|
std::shared_ptr<Font>& font = mStyle.font;
|
||||||
|
|
||||||
mGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i((int)mPrompts.size() * 4, 1));
|
mGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i((int)mPrompts.size() * 4, 1));
|
||||||
// [icon] [spacer1] [text] [spacer2]
|
|
||||||
|
|
||||||
std::vector< std::shared_ptr<ImageComponent> > icons;
|
// [icon] [spacer1] [text] [spacer2]
|
||||||
std::vector< std::shared_ptr<TextComponent> > labels;
|
|
||||||
|
|
||||||
float width = 0;
|
std::vector< std::shared_ptr<ImageComponent> > icons;
|
||||||
const float height = Math::round(font->getLetterHeight() * 1.25f);
|
std::vector< std::shared_ptr<TextComponent> > labels;
|
||||||
for(auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++)
|
|
||||||
{
|
|
||||||
auto icon = std::make_shared<ImageComponent>(mWindow);
|
|
||||||
icon->setImage(getIconTexture(it->first.c_str()));
|
|
||||||
icon->setColorShift(mStyle.iconColor);
|
|
||||||
icon->setResize(0, height);
|
|
||||||
icons.push_back(icon);
|
|
||||||
|
|
||||||
auto lbl = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(it->second), font, mStyle.textColor);
|
float width = 0;
|
||||||
labels.push_back(lbl);
|
const float height = Math::round(font->getLetterHeight() * 1.25f);
|
||||||
|
|
||||||
width += icon->getSize().x() + lbl->getSize().x() + ICON_TEXT_SPACING + ENTRY_SPACING;
|
for (auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++) {
|
||||||
}
|
auto icon = std::make_shared<ImageComponent>(mWindow);
|
||||||
|
icon->setImage(getIconTexture(it->first.c_str()));
|
||||||
|
icon->setColorShift(mStyle.iconColor);
|
||||||
|
icon->setResize(0, height);
|
||||||
|
icons.push_back(icon);
|
||||||
|
|
||||||
mGrid->setSize(width, height);
|
auto lbl = std::make_shared<TextComponent>(mWindow,
|
||||||
for(unsigned int i = 0; i < icons.size(); i++)
|
Utils::String::toUpper(it->second), font, mStyle.textColor);
|
||||||
{
|
labels.push_back(lbl);
|
||||||
const int col = i*4;
|
|
||||||
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width);
|
|
||||||
mGrid->setColWidthPerc(col + 1, ICON_TEXT_SPACING / width);
|
|
||||||
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width);
|
|
||||||
|
|
||||||
mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false);
|
width += icon->getSize().x() + lbl->getSize().x() + ICON_TEXT_SPACING + ENTRY_SPACING;
|
||||||
mGrid->setEntry(labels.at(i), Vector2i(col + 2, 0), false, false);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mGrid->setPosition(Vector3f(mStyle.position.x(), mStyle.position.y(), 0.0f));
|
mGrid->setSize(width, height);
|
||||||
//mGrid->setPosition(OFFSET_X, Renderer::getScreenHeight() - mGrid->getSize().y() - OFFSET_Y);
|
|
||||||
mGrid->setOrigin(mStyle.origin);
|
for (unsigned int i = 0; i < icons.size(); i++) {
|
||||||
|
const int col = i*4;
|
||||||
|
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x() / width);
|
||||||
|
mGrid->setColWidthPerc(col + 1, ICON_TEXT_SPACING / width);
|
||||||
|
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x() / width);
|
||||||
|
|
||||||
|
mGrid->setEntry(icons.at(i), Vector2i(col, 0), false, false);
|
||||||
|
mGrid->setEntry(labels.at(i), Vector2i(col + 2, 0), false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
mGrid->setPosition(Vector3f(mStyle.position.x(), mStyle.position.y(), 0.0f));
|
||||||
|
//mGrid->setPosition(OFFSET_X, Renderer::getScreenHeight() - mGrid->getSize().y() - OFFSET_Y);
|
||||||
|
mGrid->setOrigin(mStyle.origin);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
|
std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
|
||||||
{
|
{
|
||||||
auto it = mIconCache.find(name);
|
auto it = mIconCache.find(name);
|
||||||
if(it != mIconCache.cend())
|
if (it != mIconCache.cend())
|
||||||
return it->second;
|
return it->second;
|
||||||
|
|
||||||
auto pathLookup = ICON_PATH_MAP.find(name);
|
auto pathLookup = ICON_PATH_MAP.find(name);
|
||||||
if(pathLookup == ICON_PATH_MAP.cend())
|
if (pathLookup == ICON_PATH_MAP.cend()) {
|
||||||
{
|
LOG(LogError) << "Unknown help icon \"" << name << "\"!";
|
||||||
LOG(LogError) << "Unknown help icon \"" << name << "\"!";
|
return nullptr;
|
||||||
return nullptr;
|
}
|
||||||
}
|
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
|
||||||
if(!ResourceManager::getInstance()->fileExists(pathLookup->second))
|
LOG(LogError) << "Help icon \"" << name <<
|
||||||
{
|
"\" - corresponding image file \"" << pathLookup->second << "\" misisng!";
|
||||||
LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" misisng!";
|
return nullptr;
|
||||||
return nullptr;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> tex = TextureResource::get(pathLookup->second);
|
std::shared_ptr<TextureResource> tex = TextureResource::get(pathLookup->second);
|
||||||
mIconCache[std::string(name)] = tex;
|
mIconCache[std::string(name)] = tex;
|
||||||
return tex;
|
return tex;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HelpComponent::setOpacity(unsigned char opacity)
|
void HelpComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
GuiComponent::setOpacity(opacity);
|
GuiComponent::setOpacity(opacity);
|
||||||
|
|
||||||
for(unsigned int i = 0; i < mGrid->getChildCount(); i++)
|
for (unsigned int i = 0; i < mGrid->getChildCount(); i++)
|
||||||
{
|
mGrid->getChild(i)->setOpacity(opacity);
|
||||||
mGrid->getChild(i)->setOpacity(opacity);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void HelpComponent::render(const Transform4x4f& parentTrans)
|
void HelpComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
if(mGrid)
|
if (mGrid)
|
||||||
mGrid->render(trans);
|
mGrid->render(trans);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// HelpComponent.h
|
||||||
|
//
|
||||||
|
// Help information in icon and text pairs.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
#define ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
||||||
|
@ -12,25 +18,25 @@ class TextureResource;
|
||||||
class HelpComponent : public GuiComponent
|
class HelpComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HelpComponent(Window* window);
|
HelpComponent(Window* window);
|
||||||
|
|
||||||
void clearPrompts();
|
void clearPrompts();
|
||||||
void setPrompts(const std::vector<HelpPrompt>& prompts);
|
void setPrompts(const std::vector<HelpPrompt>& prompts);
|
||||||
|
|
||||||
void render(const Transform4x4f& parent) override;
|
void render(const Transform4x4f& parent) override;
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
|
|
||||||
void setStyle(const HelpStyle& style);
|
void setStyle(const HelpStyle& style);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::shared_ptr<TextureResource> getIconTexture(const char* name);
|
std::shared_ptr<TextureResource> getIconTexture(const char* name);
|
||||||
std::map< std::string, std::shared_ptr<TextureResource> > mIconCache;
|
std::map< std::string, std::shared_ptr<TextureResource> > mIconCache;
|
||||||
|
|
||||||
std::shared_ptr<ComponentGrid> mGrid;
|
std::shared_ptr<ComponentGrid> mGrid;
|
||||||
void updateGrid();
|
void updateGrid();
|
||||||
|
|
||||||
std::vector<HelpPrompt> mPrompts;
|
std::vector<HelpPrompt> mPrompts;
|
||||||
HelpStyle mStyle;
|
HelpStyle mStyle;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_HELP_COMPONENT_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// IList.h
|
||||||
|
//
|
||||||
|
// Gamelist base class.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
#ifndef ES_CORE_COMPONENTS_ILIST_H
|
||||||
#define ES_CORE_COMPONENTS_ILIST_H
|
#define ES_CORE_COMPONENTS_ILIST_H
|
||||||
|
@ -6,334 +12,341 @@
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
#include "PowerSaver.h"
|
#include "PowerSaver.h"
|
||||||
|
|
||||||
enum CursorState
|
enum CursorState {
|
||||||
{
|
CURSOR_STOPPED,
|
||||||
CURSOR_STOPPED,
|
CURSOR_SCROLLING
|
||||||
CURSOR_SCROLLING
|
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ListLoopType
|
enum ListLoopType {
|
||||||
{
|
LIST_ALWAYS_LOOP,
|
||||||
LIST_ALWAYS_LOOP,
|
LIST_PAUSE_AT_END,
|
||||||
LIST_PAUSE_AT_END,
|
LIST_NEVER_LOOP
|
||||||
LIST_NEVER_LOOP
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScrollTier
|
struct ScrollTier {
|
||||||
{
|
int length; // How long we stay on this tier before going to the next.
|
||||||
int length; // how long we stay on this level before going to the next
|
int scrollDelay; // How long between scrolls.
|
||||||
int scrollDelay; // how long between scrolls
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScrollTierList
|
struct ScrollTierList {
|
||||||
{
|
const int count;
|
||||||
const int count;
|
const ScrollTier* tiers;
|
||||||
const ScrollTier* tiers;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// default scroll tiers
|
// Default scroll tiers.
|
||||||
const ScrollTier QUICK_SCROLL_TIERS[] = {
|
const ScrollTier QUICK_SCROLL_TIERS[] = {
|
||||||
{500, 500},
|
{500, 500},
|
||||||
{2000, 114},
|
{2000, 114},
|
||||||
{4000, 32},
|
{4000, 32},
|
||||||
{0, 16}
|
{0, 16}
|
||||||
|
};
|
||||||
|
const ScrollTierList LIST_SCROLL_STYLE_QUICK = {
|
||||||
|
4,
|
||||||
|
QUICK_SCROLL_TIERS
|
||||||
};
|
};
|
||||||
const ScrollTierList LIST_SCROLL_STYLE_QUICK = { 4, QUICK_SCROLL_TIERS };
|
|
||||||
|
|
||||||
const ScrollTier SLOW_SCROLL_TIERS[] = {
|
const ScrollTier SLOW_SCROLL_TIERS[] = {
|
||||||
{500, 500},
|
{500, 500},
|
||||||
{0, 200}
|
{0, 200}
|
||||||
};
|
};
|
||||||
|
|
||||||
const ScrollTierList LIST_SCROLL_STYLE_SLOW = { 2, SLOW_SCROLL_TIERS };
|
const ScrollTierList LIST_SCROLL_STYLE_SLOW = { 2, SLOW_SCROLL_TIERS };
|
||||||
|
|
||||||
template <typename EntryData, typename UserData>
|
template <typename EntryData, typename UserData>
|
||||||
class IList : public GuiComponent
|
class IList : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
struct Entry
|
struct Entry {
|
||||||
{
|
std::string name;
|
||||||
std::string name;
|
UserData object;
|
||||||
UserData object;
|
EntryData data;
|
||||||
EntryData data;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
int mCursor;
|
int mCursor;
|
||||||
|
|
||||||
int mScrollTier;
|
int mScrollTier;
|
||||||
int mScrollVelocity;
|
int mScrollVelocity;
|
||||||
|
|
||||||
int mScrollTierAccumulator;
|
int mScrollTierAccumulator;
|
||||||
int mScrollCursorAccumulator;
|
int mScrollCursorAccumulator;
|
||||||
|
|
||||||
unsigned char mTitleOverlayOpacity;
|
unsigned char mTitleOverlayOpacity;
|
||||||
unsigned int mTitleOverlayColor;
|
unsigned int mTitleOverlayColor;
|
||||||
ImageComponent mGradient;
|
ImageComponent mGradient;
|
||||||
std::shared_ptr<Font> mTitleOverlayFont;
|
std::shared_ptr<Font> mTitleOverlayFont;
|
||||||
|
|
||||||
const ScrollTierList& mTierList;
|
const ScrollTierList& mTierList;
|
||||||
const ListLoopType mLoopType;
|
const ListLoopType mLoopType;
|
||||||
|
|
||||||
std::vector<Entry> mEntries;
|
std::vector<Entry> mEntries;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
IList(Window* window, const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK, const ListLoopType& loopType = LIST_PAUSE_AT_END) : GuiComponent(window),
|
IList(
|
||||||
mGradient(window), mTierList(tierList), mLoopType(loopType)
|
Window* window,
|
||||||
{
|
const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK,
|
||||||
mCursor = 0;
|
const ListLoopType& loopType = LIST_PAUSE_AT_END)
|
||||||
mScrollTier = 0;
|
: GuiComponent(window),
|
||||||
mScrollVelocity = 0;
|
mGradient(window),
|
||||||
mScrollTierAccumulator = 0;
|
mTierList(tierList),
|
||||||
mScrollCursorAccumulator = 0;
|
mLoopType(loopType)
|
||||||
|
{
|
||||||
|
mCursor = 0;
|
||||||
|
mScrollTier = 0;
|
||||||
|
mScrollVelocity = 0;
|
||||||
|
mScrollTierAccumulator = 0;
|
||||||
|
mScrollCursorAccumulator = 0;
|
||||||
|
|
||||||
mTitleOverlayOpacity = 0x00;
|
mTitleOverlayOpacity = 0x00;
|
||||||
mTitleOverlayColor = 0xFFFFFF00;
|
mTitleOverlayColor = 0xFFFFFF00;
|
||||||
mGradient.setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
mGradient.setResize((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||||
mGradient.setImage(":/graphics/scroll_gradient.png");
|
mGradient.setImage(":/graphics/scroll_gradient.png");
|
||||||
mTitleOverlayFont = Font::get(FONT_SIZE_LARGE);
|
mTitleOverlayFont = Font::get(FONT_SIZE_LARGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
{
|
{
|
||||||
listInput(0);
|
listInput(0);
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
mEntries.clear();
|
mEntries.clear();
|
||||||
mCursor = 0;
|
mCursor = 0;
|
||||||
listInput(0);
|
listInput(0);
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const std::string& getSelectedName()
|
inline 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
|
inline const UserData& getSelected() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.at(mCursor).object;
|
return mEntries.at(mCursor).object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getFirst() const
|
inline const UserData& getFirst() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.front().object;
|
return mEntries.front().object;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline const UserData& getLast() const
|
inline const UserData& getLast() const
|
||||||
{
|
{
|
||||||
assert(size() > 0);
|
assert(size() > 0);
|
||||||
return mEntries.back().object;
|
return mEntries.back().object;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setCursor(typename std::vector<Entry>::const_iterator& it)
|
void setCursor(typename std::vector<Entry>::const_iterator& it)
|
||||||
{
|
{
|
||||||
assert(it != mEntries.cend());
|
assert(it != mEntries.cend());
|
||||||
mCursor = it - mEntries.cbegin();
|
mCursor = it - mEntries.cbegin();
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns true if successful (select is in our list), false if not
|
// Returns true if successful (select is in our list), false if not.
|
||||||
bool setCursor(const UserData& obj)
|
bool setCursor(const UserData& obj)
|
||||||
{
|
{
|
||||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
|
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
||||||
{
|
if ((*it).object == obj) {
|
||||||
if((*it).object == obj)
|
mCursor = (int)(it - mEntries.cbegin());
|
||||||
{
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
mCursor = (int)(it - mEntries.cbegin());
|
return true;
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
}
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
|
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
||||||
{
|
if ((*it).object == obj) {
|
||||||
if((*it).object == obj)
|
remove(it);
|
||||||
{
|
return true;
|
||||||
remove(it);
|
}
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int size() const { return (int)mEntries.size(); }
|
inline int size() const { return (int)mEntries.size(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void remove(typename std::vector<Entry>::const_iterator& it)
|
void remove(typename std::vector<Entry>::const_iterator& it)
|
||||||
{
|
{
|
||||||
if(mCursor > 0 && it - mEntries.cbegin() <= mCursor)
|
if (mCursor > 0 && it - mEntries.cbegin() <= mCursor) {
|
||||||
{
|
mCursor--;
|
||||||
mCursor--;
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mEntries.erase(it);
|
mEntries.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool listFirstRow()
|
bool listFirstRow()
|
||||||
{
|
{
|
||||||
mCursor = 0;
|
mCursor = 0;
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
onScroll();
|
onScroll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool listLastRow()
|
bool listLastRow()
|
||||||
{
|
{
|
||||||
mCursor = mEntries.size() - 1;
|
mCursor = mEntries.size() - 1;
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
onScroll();
|
onScroll();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool listInput(int velocity) // a velocity of 0 = stop scrolling
|
|
||||||
{
|
|
||||||
PowerSaver::setState(velocity == 0);
|
|
||||||
|
|
||||||
// generate an onCursorChanged event in the stopped state when the user lets go of the key
|
bool listInput(int velocity) // A velocity of 0 = stop scrolling.
|
||||||
if(velocity == 0 && mScrollVelocity != 0)
|
{
|
||||||
onCursorChanged(CURSOR_STOPPED);
|
PowerSaver::setState(velocity == 0);
|
||||||
|
|
||||||
mScrollVelocity = velocity;
|
// Generate an onCursorChanged event in the stopped state when the user
|
||||||
mScrollTier = 0;
|
// lets go of the key.
|
||||||
mScrollTierAccumulator = 0;
|
if (velocity == 0 && mScrollVelocity != 0)
|
||||||
mScrollCursorAccumulator = 0;
|
onCursorChanged(CURSOR_STOPPED);
|
||||||
|
|
||||||
int prevCursor = mCursor;
|
mScrollVelocity = velocity;
|
||||||
scroll(mScrollVelocity);
|
mScrollTier = 0;
|
||||||
return (prevCursor != mCursor);
|
mScrollTierAccumulator = 0;
|
||||||
}
|
mScrollCursorAccumulator = 0;
|
||||||
|
|
||||||
void listUpdate(int deltaTime)
|
int prevCursor = mCursor;
|
||||||
{
|
scroll(mScrollVelocity);
|
||||||
// update the title overlay opacity
|
return (prevCursor != mCursor);
|
||||||
const int dir = (mScrollTier >= mTierList.count - 1) ? 1 : -1; // fade in if scroll tier is >= 1, otherwise fade out
|
}
|
||||||
int op = mTitleOverlayOpacity + deltaTime*dir; // we just do a 1-to-1 time -> opacity, no scaling
|
|
||||||
if(op >= 255)
|
|
||||||
mTitleOverlayOpacity = 255;
|
|
||||||
else if(op <= 0)
|
|
||||||
mTitleOverlayOpacity = 0;
|
|
||||||
else
|
|
||||||
mTitleOverlayOpacity = (unsigned char)op;
|
|
||||||
|
|
||||||
if(mScrollVelocity == 0 || size() < 2)
|
void listUpdate(int deltaTime)
|
||||||
return;
|
{
|
||||||
|
// Update the title overlay opacity.
|
||||||
|
// Fade in if scroll tier is >= 1, otherwise fade out.
|
||||||
|
const int dir = (mScrollTier >= mTierList.count - 1) ? 1 : -1;
|
||||||
|
// We just do a 1-to-1 time -> opacity, no scaling.
|
||||||
|
int op = mTitleOverlayOpacity + deltaTime*dir;
|
||||||
|
if (op >= 255)
|
||||||
|
mTitleOverlayOpacity = 255;
|
||||||
|
else if (op <= 0)
|
||||||
|
mTitleOverlayOpacity = 0;
|
||||||
|
else
|
||||||
|
mTitleOverlayOpacity = (unsigned char)op;
|
||||||
|
|
||||||
mScrollCursorAccumulator += deltaTime;
|
if (mScrollVelocity == 0 || size() < 2)
|
||||||
mScrollTierAccumulator += deltaTime;
|
return;
|
||||||
|
|
||||||
// we delay scrolling until after scroll tier has updated so isScrolling() returns accurately during onCursorChanged callbacks
|
mScrollCursorAccumulator += deltaTime;
|
||||||
// we don't just do scroll tier first because it would not catch the scrollDelay == tier length case
|
mScrollTierAccumulator += deltaTime;
|
||||||
int scrollCount = 0;
|
|
||||||
while(mScrollCursorAccumulator >= mTierList.tiers[mScrollTier].scrollDelay)
|
|
||||||
{
|
|
||||||
mScrollCursorAccumulator -= mTierList.tiers[mScrollTier].scrollDelay;
|
|
||||||
scrollCount++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// are we ready to go even FASTER?
|
// We delay scrolling until after scroll tier has updated so isScrolling() returns
|
||||||
while(mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >= mTierList.tiers[mScrollTier].length)
|
// accurately during onCursorChanged callbacks. We don't just do scroll tier first
|
||||||
{
|
// because it would not catch the scrollDelay == tier length case.
|
||||||
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
|
int scrollCount = 0;
|
||||||
mScrollTier++;
|
while (mScrollCursorAccumulator >= mTierList.tiers[mScrollTier].scrollDelay) {
|
||||||
}
|
mScrollCursorAccumulator -= mTierList.tiers[mScrollTier].scrollDelay;
|
||||||
|
scrollCount++;
|
||||||
|
}
|
||||||
|
|
||||||
// actually perform the scrolling
|
// Are we ready to go even FASTER?
|
||||||
for(int i = 0; i < scrollCount; i++)
|
while (mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >=
|
||||||
scroll(mScrollVelocity);
|
mTierList.tiers[mScrollTier].length) {
|
||||||
}
|
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
|
||||||
|
mScrollTier++;
|
||||||
|
}
|
||||||
|
|
||||||
void listRenderTitleOverlay(const Transform4x4f& /*trans*/)
|
// Actually perform the scrolling.
|
||||||
{
|
for (int i = 0; i < scrollCount; i++)
|
||||||
if(size() == 0 || !mTitleOverlayFont || mTitleOverlayOpacity == 0)
|
scroll(mScrollVelocity);
|
||||||
return;
|
}
|
||||||
|
|
||||||
// we don't bother caching this because it's only two letters and will change pretty much every frame if we're scrolling
|
void listRenderTitleOverlay(const Transform4x4f& /*trans*/)
|
||||||
const std::string text = getSelectedName().size() >= 2 ? getSelectedName().substr(0, 2) : "??";
|
{
|
||||||
|
if (size() == 0 || !mTitleOverlayFont || mTitleOverlayOpacity == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
Vector2f off = mTitleOverlayFont->sizeText(text);
|
// We don't bother caching this because it's only two letters and will change pretty
|
||||||
off[0] = (Renderer::getScreenWidth() - off.x()) * 0.5f;
|
// much every frame if we're scrolling.
|
||||||
off[1] = (Renderer::getScreenHeight() - off.y()) * 0.5f;
|
const std::string text = getSelectedName().size() >= 2 ?
|
||||||
|
getSelectedName().substr(0, 2) : "??";
|
||||||
|
|
||||||
Transform4x4f identTrans = Transform4x4f::Identity();
|
Vector2f off = mTitleOverlayFont->sizeText(text);
|
||||||
|
off[0] = (Renderer::getScreenWidth() - off.x()) * 0.5f;
|
||||||
|
off[1] = (Renderer::getScreenHeight() - off.y()) * 0.5f;
|
||||||
|
|
||||||
mGradient.setOpacity(mTitleOverlayOpacity);
|
Transform4x4f identTrans = Transform4x4f::Identity();
|
||||||
mGradient.render(identTrans);
|
|
||||||
|
|
||||||
TextCache* cache = mTitleOverlayFont->buildTextCache(text, off.x(), off.y(), 0xFFFFFF00 | mTitleOverlayOpacity);
|
mGradient.setOpacity(mTitleOverlayOpacity);
|
||||||
mTitleOverlayFont->renderTextCache(cache); // relies on mGradient's render for Renderer::setMatrix()
|
mGradient.render(identTrans);
|
||||||
delete cache;
|
|
||||||
}
|
|
||||||
|
|
||||||
void scroll(int amt)
|
TextCache* cache = mTitleOverlayFont->buildTextCache(text, off.x(), off.y(),
|
||||||
{
|
0xFFFFFF00 | mTitleOverlayOpacity);
|
||||||
if(mScrollVelocity == 0 || size() < 2)
|
// Relies on mGradient's render for Renderer::setMatrix()
|
||||||
return;
|
mTitleOverlayFont->renderTextCache(cache);
|
||||||
|
delete cache;
|
||||||
|
}
|
||||||
|
|
||||||
int cursor = mCursor + amt;
|
void scroll(int amt)
|
||||||
int absAmt = amt < 0 ? -amt : amt;
|
{
|
||||||
|
if (mScrollVelocity == 0 || size() < 2)
|
||||||
|
return;
|
||||||
|
|
||||||
// stop at the end if we've been holding down the button for a long time or
|
int cursor = mCursor + amt;
|
||||||
// we're scrolling faster than one item at a time (e.g. page up/down)
|
int absAmt = amt < 0 ? -amt : amt;
|
||||||
// otherwise, loop around
|
|
||||||
if((mLoopType == LIST_PAUSE_AT_END && (mScrollTier > 0 || absAmt > 1)) ||
|
|
||||||
mLoopType == LIST_NEVER_LOOP)
|
|
||||||
{
|
|
||||||
if(cursor < 0)
|
|
||||||
{
|
|
||||||
cursor = 0;
|
|
||||||
mScrollVelocity = 0;
|
|
||||||
mScrollTier = 0;
|
|
||||||
}else if(cursor >= size())
|
|
||||||
{
|
|
||||||
cursor = size() - 1;
|
|
||||||
mScrollVelocity = 0;
|
|
||||||
mScrollTier = 0;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
while(cursor < 0)
|
|
||||||
cursor += size();
|
|
||||||
while(cursor >= size())
|
|
||||||
cursor -= size();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cursor != mCursor)
|
// Stop at the end if we've been holding down the button for a long time or
|
||||||
onScroll();
|
// we're scrolling faster than one item at a time (e.g. page up/down).
|
||||||
|
// Otherwise, loop around.
|
||||||
|
if ((mLoopType == LIST_PAUSE_AT_END && (mScrollTier > 0 || absAmt > 1)) ||
|
||||||
|
mLoopType == LIST_NEVER_LOOP) {
|
||||||
|
if (cursor < 0) {
|
||||||
|
cursor = 0;
|
||||||
|
mScrollVelocity = 0;
|
||||||
|
mScrollTier = 0;
|
||||||
|
}
|
||||||
|
else if (cursor >= size()) {
|
||||||
|
cursor = size() - 1;
|
||||||
|
mScrollVelocity = 0;
|
||||||
|
mScrollTier = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
while (cursor < 0)
|
||||||
|
cursor += size();
|
||||||
|
while (cursor >= size())
|
||||||
|
cursor -= size();
|
||||||
|
}
|
||||||
|
|
||||||
mCursor = cursor;
|
if (cursor != mCursor)
|
||||||
onCursorChanged((mScrollTier > 0) ? CURSOR_SCROLLING : CURSOR_STOPPED);
|
onScroll();
|
||||||
}
|
|
||||||
|
|
||||||
virtual void onCursorChanged(const CursorState& /*state*/) {}
|
mCursor = cursor;
|
||||||
virtual void onScroll() {}
|
onCursorChanged((mScrollTier > 0) ? CURSOR_SCROLLING : CURSOR_STOPPED);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void onCursorChanged(const CursorState& /*state*/) {}
|
||||||
|
virtual void onScroll() {}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_ILIST_H
|
#endif // ES_CORE_COMPONENTS_ILIST_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// ImageComponent.cpp
|
||||||
|
//
|
||||||
|
// Handles images: loading, resizing, cropping, color shifting etc.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/ImageComponent.h"
|
#include "components/ImageComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
|
@ -7,23 +13,39 @@
|
||||||
|
|
||||||
Vector2i ImageComponent::getTextureSize() const
|
Vector2i ImageComponent::getTextureSize() const
|
||||||
{
|
{
|
||||||
if(mTexture)
|
if (mTexture)
|
||||||
return mTexture->getSize();
|
return mTexture->getSize();
|
||||||
else
|
else
|
||||||
return Vector2i::Zero();
|
return Vector2i::Zero();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f ImageComponent::getSize() const
|
Vector2f ImageComponent::getSize() const
|
||||||
{
|
{
|
||||||
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
|
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageComponent::ImageComponent(Window* window, bool forceLoad, bool dynamic) : GuiComponent(window),
|
ImageComponent::ImageComponent(
|
||||||
mTargetIsMax(false), mTargetIsMin(false), mFlipX(false), mFlipY(false), mTargetSize(0, 0), mColorShift(0xFFFFFFFF),
|
Window* window,
|
||||||
mColorShiftEnd(0xFFFFFFFF), mColorGradientHorizontal(true), mForceLoad(forceLoad), mDynamic(dynamic),
|
bool forceLoad,
|
||||||
mFadeOpacity(0), mFading(false), mRotateByTargetSize(false), mTopLeftCrop(0.0f, 0.0f), mBottomRightCrop(1.0f, 1.0f)
|
bool dynamic)
|
||||||
|
: GuiComponent(window),
|
||||||
|
mTargetIsMax(false),
|
||||||
|
mTargetIsMin(false),
|
||||||
|
mFlipX(false),
|
||||||
|
mFlipY(false),
|
||||||
|
mTargetSize(0, 0),
|
||||||
|
mColorShift(0xFFFFFFFF),
|
||||||
|
mColorShiftEnd(0xFFFFFFFF),
|
||||||
|
mColorGradientHorizontal(true),
|
||||||
|
mForceLoad(forceLoad),
|
||||||
|
mDynamic(dynamic),
|
||||||
|
mFadeOpacity(0),
|
||||||
|
mFading(false),
|
||||||
|
mRotateByTargetSize(false),
|
||||||
|
mTopLeftCrop(0.0f, 0.0f),
|
||||||
|
mBottomRightCrop(1.0f, 1.0f)
|
||||||
{
|
{
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageComponent::~ImageComponent()
|
ImageComponent::~ImageComponent()
|
||||||
|
@ -32,412 +54,406 @@ ImageComponent::~ImageComponent()
|
||||||
|
|
||||||
void ImageComponent::resize()
|
void ImageComponent::resize()
|
||||||
{
|
{
|
||||||
if(!mTexture)
|
if (!mTexture)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Vector2f textureSize = mTexture->getSourceImageSize();
|
const Vector2f textureSize = mTexture->getSourceImageSize();
|
||||||
if(textureSize == Vector2f::Zero())
|
if (textureSize == Vector2f::Zero())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if(mTexture->isTiled())
|
if (mTexture->isTiled()) {
|
||||||
{
|
mSize = mTargetSize;
|
||||||
mSize = mTargetSize;
|
}
|
||||||
}else{
|
else {
|
||||||
// SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels
|
// SVG rasterization is determined by height and rasterization is done in terms of pixels.
|
||||||
// if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks
|
// If rounding is off enough in the rasterization step (for images with extreme aspect
|
||||||
// so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that
|
// ratios), it can cause cutoff when the aspect ratio breaks.
|
||||||
// (you'll see this scattered throughout the function)
|
// So we always make sure the resultant height is an integer to make sure cutoff doesn't
|
||||||
// this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request!
|
// happen, and scale width from that (you'll see this scattered throughout the function).
|
||||||
|
// This is probably not the best way, so if you're familiar with this problem and have a
|
||||||
|
// better solution, please make a pull request!
|
||||||
|
if (mTargetIsMax) {
|
||||||
|
mSize = textureSize;
|
||||||
|
|
||||||
if(mTargetIsMax)
|
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
||||||
{
|
|
||||||
mSize = textureSize;
|
|
||||||
|
|
||||||
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
if (resizeScale.x() < resizeScale.y()) {
|
||||||
|
// This will be mTargetSize.x(). We can't exceed it, nor be lower than it.
|
||||||
|
mSize[0] *= resizeScale.x();
|
||||||
|
// We need to make sure we're not creating an image larger than max size.
|
||||||
|
mSize[1] = Math::min(Math::round(mSize[1] *= resizeScale.x()), mTargetSize.y());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// This will be mTargetSize.y(). We can't exceed it.
|
||||||
|
mSize[1] = Math::round(mSize[1] * resizeScale.y());
|
||||||
|
// 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.
|
||||||
|
mSize[0] = Math::min((mSize[1] / textureSize.y()) * textureSize.x(),
|
||||||
|
mTargetSize.x());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (mTargetIsMin) {
|
||||||
|
mSize = textureSize;
|
||||||
|
|
||||||
if(resizeScale.x() < resizeScale.y())
|
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
||||||
{
|
|
||||||
mSize[0] *= resizeScale.x(); // this will be mTargetSize.x(). We can't exceed it, nor be lower than it.
|
|
||||||
// we need to make sure we're not creating an image larger than max size
|
|
||||||
mSize[1] = Math::min(Math::round(mSize[1] *= resizeScale.x()), mTargetSize.y());
|
|
||||||
}else{
|
|
||||||
mSize[1] = Math::round(mSize[1] * resizeScale.y()); // this will be mTargetSize.y(). We can't exceed it.
|
|
||||||
|
|
||||||
// for SVG rasterization, always calculate width from rounded height (see comment above)
|
if (resizeScale.x() > resizeScale.y()) {
|
||||||
// we need to make sure we're not creating an image larger than max size
|
mSize[0] *= resizeScale.x();
|
||||||
mSize[0] = Math::min((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
mSize[1] *= resizeScale.x();
|
||||||
}
|
|
||||||
}else if(mTargetIsMin)
|
|
||||||
{
|
|
||||||
mSize = textureSize;
|
|
||||||
|
|
||||||
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
float cropPercent = (mSize.y() - mTargetSize.y()) / (mSize.y() * 2);
|
||||||
|
crop(0, cropPercent, 0, cropPercent);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mSize[0] *= resizeScale.y();
|
||||||
|
mSize[1] *= resizeScale.y();
|
||||||
|
|
||||||
if(resizeScale.x() > resizeScale.y())
|
float cropPercent = (mSize.x() - mTargetSize.x()) / (mSize.x() * 2);
|
||||||
{
|
crop(cropPercent, 0, cropPercent, 0);
|
||||||
mSize[0] *= resizeScale.x();
|
}
|
||||||
mSize[1] *= resizeScale.x();
|
// For SVG rasterization, always calculate width from rounded height (see comment
|
||||||
|
// above). We need to make sure we're not creating an image smaller than min size.
|
||||||
|
mSize[1] = Math::max(Math::round(mSize[1]), mTargetSize.y());
|
||||||
|
mSize[0] = Math::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
||||||
|
|
||||||
float cropPercent = (mSize.y() - mTargetSize.y()) / (mSize.y() * 2);
|
}
|
||||||
crop(0, cropPercent, 0, cropPercent);
|
else {
|
||||||
}else{
|
// If both components are set, we just stretch.
|
||||||
mSize[0] *= resizeScale.y();
|
// If no components are set, we don't resize at all.
|
||||||
mSize[1] *= resizeScale.y();
|
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
|
||||||
|
|
||||||
float cropPercent = (mSize.x() - mTargetSize.x()) / (mSize.x() * 2);
|
// If only one component is set, we resize in a way that maintains aspect ratio.
|
||||||
crop(cropPercent, 0, cropPercent, 0);
|
// For SVG rasterization, we always calculate width from rounded height (see
|
||||||
}
|
// comment above).
|
||||||
|
if (!mTargetSize.x() && mTargetSize.y()) {
|
||||||
|
mSize[1] = Math::round(mTargetSize.y());
|
||||||
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
||||||
|
}
|
||||||
|
else if (mTargetSize.x() && !mTargetSize.y()) {
|
||||||
|
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
||||||
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// for SVG rasterization, always calculate width from rounded height (see comment above)
|
mSize[0] = Math::round(mSize.x());
|
||||||
// we need to make sure we're not creating an image smaller than min size
|
mSize[1] = Math::round(mSize.y());
|
||||||
mSize[1] = Math::max(Math::round(mSize[1]), mTargetSize.y());
|
// mSize.y() should already be rounded.
|
||||||
mSize[0] = Math::max((mSize[1] / textureSize.y()) * textureSize.x(), mTargetSize.x());
|
mTexture->rasterizeAt((size_t)mSize.x(), (size_t)mSize.y());
|
||||||
|
|
||||||
}else{
|
onSizeChanged();
|
||||||
// if both components are set, we just stretch
|
|
||||||
// if no components are set, we don't resize at all
|
|
||||||
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
|
|
||||||
|
|
||||||
// if only one component is set, we resize in a way that maintains aspect ratio
|
|
||||||
// for SVG rasterization, we always calculate width from rounded height (see comment above)
|
|
||||||
if(!mTargetSize.x() && mTargetSize.y())
|
|
||||||
{
|
|
||||||
mSize[1] = Math::round(mTargetSize.y());
|
|
||||||
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
||||||
}else if(mTargetSize.x() && !mTargetSize.y())
|
|
||||||
{
|
|
||||||
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
|
||||||
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mSize[0] = Math::round(mSize.x());
|
|
||||||
mSize[1] = Math::round(mSize.y());
|
|
||||||
// mSize.y() should already be rounded
|
|
||||||
mTexture->rasterizeAt((size_t)mSize.x(), (size_t)mSize.y());
|
|
||||||
|
|
||||||
onSizeChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::onSizeChanged()
|
void ImageComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
updateVertices();
|
updateVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setDefaultImage(std::string path)
|
void ImageComponent::setDefaultImage(std::string path)
|
||||||
{
|
{
|
||||||
mDefaultPath = path;
|
mDefaultPath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setImage(std::string path, bool tile)
|
void ImageComponent::setImage(std::string path, bool tile)
|
||||||
{
|
{
|
||||||
if(path.empty() || !ResourceManager::getInstance()->fileExists(path))
|
if (path.empty() || !ResourceManager::getInstance()->fileExists(path)) {
|
||||||
{
|
if (mDefaultPath.empty() || !ResourceManager::getInstance()->fileExists(mDefaultPath))
|
||||||
if(mDefaultPath.empty() || !ResourceManager::getInstance()->fileExists(mDefaultPath))
|
mTexture.reset();
|
||||||
mTexture.reset();
|
else
|
||||||
else
|
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic);
|
||||||
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic);
|
}
|
||||||
} else {
|
else {
|
||||||
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic);
|
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setImage(const char* path, size_t length, bool tile)
|
void ImageComponent::setImage(const char* path, size_t length, bool tile)
|
||||||
{
|
{
|
||||||
mTexture.reset();
|
mTexture.reset();
|
||||||
|
|
||||||
mTexture = TextureResource::get("", tile);
|
mTexture = TextureResource::get("", tile);
|
||||||
mTexture->initFromMemory(path, length);
|
mTexture->initFromMemory(path, length);
|
||||||
|
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setImage(const std::shared_ptr<TextureResource>& texture)
|
void ImageComponent::setImage(const std::shared_ptr<TextureResource>& texture)
|
||||||
{
|
{
|
||||||
mTexture = texture;
|
mTexture = texture;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setResize(float width, float height)
|
void ImageComponent::setResize(float width, float height)
|
||||||
{
|
{
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
mTargetIsMin = false;
|
mTargetIsMin = false;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setMaxSize(float width, float height)
|
void ImageComponent::setMaxSize(float width, float height)
|
||||||
{
|
{
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = true;
|
mTargetIsMax = true;
|
||||||
mTargetIsMin = false;
|
mTargetIsMin = false;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setMinSize(float width, float height)
|
void ImageComponent::setMinSize(float width, float height)
|
||||||
{
|
{
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
mTargetIsMin = true;
|
mTargetIsMin = true;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f ImageComponent::getRotationSize() const
|
Vector2f ImageComponent::getRotationSize() const
|
||||||
{
|
{
|
||||||
return mRotateByTargetSize ? mTargetSize : mSize;
|
return mRotateByTargetSize ? mTargetSize : mSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setRotateByTargetSize(bool rotate)
|
void ImageComponent::setRotateByTargetSize(bool rotate)
|
||||||
{
|
{
|
||||||
mRotateByTargetSize = 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);
|
||||||
mTopLeftCrop.x() = percent;
|
mTopLeftCrop.x() = percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropTop(float percent)
|
void ImageComponent::cropTop(float percent)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(percent >= 0.0f && percent <= 1.0f);
|
||||||
mTopLeftCrop.y() = percent;
|
mTopLeftCrop.y() = percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropRight(float percent)
|
void ImageComponent::cropRight(float percent)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(percent >= 0.0f && percent <= 1.0f);
|
||||||
mBottomRightCrop.x() = 1.0f - percent;
|
mBottomRightCrop.x() = 1.0f - percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::cropBot(float percent)
|
void ImageComponent::cropBot(float percent)
|
||||||
{
|
{
|
||||||
assert(percent >= 0.0f && percent <= 1.0f);
|
assert(percent >= 0.0f && percent <= 1.0f);
|
||||||
mBottomRightCrop.y() = 1.0f - percent;
|
mBottomRightCrop.y() = 1.0f - percent;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::crop(float left, float top, float right, float bot)
|
void ImageComponent::crop(float left, float top, float right, float bot)
|
||||||
{
|
{
|
||||||
cropLeft(left);
|
cropLeft(left);
|
||||||
cropTop(top);
|
cropTop(top);
|
||||||
cropRight(right);
|
cropRight(right);
|
||||||
cropBot(bot);
|
cropBot(bot);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::uncrop()
|
void ImageComponent::uncrop()
|
||||||
{
|
{
|
||||||
crop(0, 0, 0, 0);
|
crop(0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setFlipX(bool flip)
|
void ImageComponent::setFlipX(bool flip)
|
||||||
{
|
{
|
||||||
mFlipX = flip;
|
mFlipX = flip;
|
||||||
updateVertices();
|
updateVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setFlipY(bool flip)
|
void ImageComponent::setFlipY(bool flip)
|
||||||
{
|
{
|
||||||
mFlipY = flip;
|
mFlipY = flip;
|
||||||
updateVertices();
|
updateVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setColorShift(unsigned int color)
|
void ImageComponent::setColorShift(unsigned int color)
|
||||||
{
|
{
|
||||||
mColorShift = color;
|
mColorShift = color;
|
||||||
mColorShiftEnd = color;
|
mColorShiftEnd = color;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setColorShiftEnd(unsigned int color)
|
void ImageComponent::setColorShiftEnd(unsigned int color)
|
||||||
{
|
{
|
||||||
mColorShiftEnd = color;
|
mColorShiftEnd = color;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setColorGradientHorizontal(bool horizontal)
|
void ImageComponent::setColorGradientHorizontal(bool horizontal)
|
||||||
{
|
{
|
||||||
mColorGradientHorizontal = horizontal;
|
mColorGradientHorizontal = horizontal;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::setOpacity(unsigned char opacity)
|
void ImageComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
mOpacity = opacity;
|
mOpacity = opacity;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::updateVertices()
|
void ImageComponent::updateVertices()
|
||||||
{
|
{
|
||||||
if(!mTexture || !mTexture->isInitialized())
|
if (!mTexture || !mTexture->isInitialized())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// we go through this mess to make sure everything is properly rounded
|
// We go through this mess to make sure everything is properly rounded.
|
||||||
// if we just round vertices at the end, edge cases occur near sizes of 0.5
|
// If we just round vertices at the end, edge cases occur near sizes of 0.5.
|
||||||
const Vector2f topLeft = { mSize * mTopLeftCrop };
|
const Vector2f topLeft = { mSize * mTopLeftCrop };
|
||||||
const Vector2f bottomRight = { mSize * mBottomRightCrop };
|
const Vector2f bottomRight = { mSize * mBottomRightCrop };
|
||||||
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;
|
||||||
|
|
||||||
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 };
|
||||||
|
|
||||||
updateColors();
|
updateColors();
|
||||||
|
|
||||||
// round vertices
|
// Round vertices.
|
||||||
for(int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
mVertices[i].pos.round();
|
mVertices[i].pos.round();
|
||||||
|
|
||||||
if(mFlipX)
|
if (mFlipX) {
|
||||||
{
|
for (int i = 0; i < 4; ++i)
|
||||||
for(int i = 0; i < 4; ++i)
|
mVertices[i].tex[0] = px - mVertices[i].tex[0];
|
||||||
mVertices[i].tex[0] = px - mVertices[i].tex[0];
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(mFlipY)
|
if (mFlipY) {
|
||||||
{
|
for (int i = 0; i < 4; ++i)
|
||||||
for(int i = 0; i < 4; ++i)
|
mVertices[i].tex[1] = py - mVertices[i].tex[1];
|
||||||
mVertices[i].tex[1] = py - mVertices[i].tex[1];
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::updateColors()
|
void ImageComponent::updateColors()
|
||||||
{
|
{
|
||||||
const float opacity = (mOpacity * (mFading ? mFadeOpacity / 255.0 : 1.0)) / 255.0;
|
const float opacity = (mOpacity * (mFading ? mFadeOpacity / 255.0 : 1.0)) / 255.0;
|
||||||
const unsigned int color = Renderer::convertColor(mColorShift & 0xFFFFFF00 | (unsigned char)((mColorShift & 0xFF) * opacity));
|
const unsigned int color = Renderer::convertColor(mColorShift & 0xFFFFFF00 |
|
||||||
const unsigned int colorEnd = Renderer::convertColor(mColorShiftEnd & 0xFFFFFF00 | (unsigned char)((mColorShiftEnd & 0xFF) * opacity));
|
(unsigned char)((mColorShift & 0xFF) * opacity));
|
||||||
|
const unsigned int colorEnd = Renderer::convertColor(mColorShiftEnd & 0xFFFFFF00 |
|
||||||
|
(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;
|
||||||
mVertices[2].col = mColorGradientHorizontal ? color : colorEnd;
|
mVertices[2].col = mColorGradientHorizontal ? color : colorEnd;
|
||||||
mVertices[3].col = colorEnd;
|
mVertices[3].col = colorEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::render(const Transform4x4f& parentTrans)
|
void ImageComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
if(mTexture && mOpacity > 0)
|
if (mTexture && mOpacity > 0) {
|
||||||
{
|
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(), 0x00000033, 0x00000033);
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x00000033, 0x00000033);
|
||||||
}
|
}
|
||||||
if(mTexture->isInitialized())
|
if (mTexture->isInitialized()) {
|
||||||
{
|
// Actually draw the image.
|
||||||
// actually draw the image
|
// The bind() function returns false if the texture is not currently loaded. A blank
|
||||||
// The bind() function returns false if the texture is not currently loaded. A blank
|
// 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
|
// 'jump' in when it finally loads.
|
||||||
// when it finally loads
|
fadeIn(mTexture->bind());
|
||||||
fadeIn(mTexture->bind());
|
Renderer::drawTriangleStrips(&mVertices[0], 4);
|
||||||
Renderer::drawTriangleStrips(&mVertices[0], 4);
|
|
||||||
|
|
||||||
}else{
|
}
|
||||||
LOG(LogError) << "Image texture is not initialized!";
|
else {
|
||||||
mTexture.reset();
|
LOG(LogError) << "Image texture is not initialized!";
|
||||||
}
|
mTexture.reset();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::fadeIn(bool textureLoaded)
|
void ImageComponent::fadeIn(bool textureLoaded)
|
||||||
{
|
{
|
||||||
if (!mForceLoad)
|
if (!mForceLoad) {
|
||||||
{
|
if (!textureLoaded) {
|
||||||
if (!textureLoaded)
|
// Start the fade if this is the first time we've encountered the unloaded texture.
|
||||||
{
|
if (!mFading) {
|
||||||
// Start the fade if this is the first time we've encountered the unloaded texture
|
// Start with a zero opacity and flag it as fading.
|
||||||
if (!mFading)
|
mFadeOpacity = 0;
|
||||||
{
|
mFading = true;
|
||||||
// Start with a zero opacity and flag it as fading
|
updateColors();
|
||||||
mFadeOpacity = 0;
|
}
|
||||||
mFading = true;
|
}
|
||||||
updateColors();
|
else if (mFading) {
|
||||||
}
|
// The texture is loaded and we need to fade it in. The fade is based on the frame
|
||||||
}
|
// rate and is 1/4 second if running at 60 frames per second although the actual
|
||||||
else if (mFading)
|
// value is not that important.
|
||||||
{
|
int opacity = mFadeOpacity + 255 / 15;
|
||||||
// The texture is loaded and we need to fade it in. The fade is based on the frame rate
|
// See if we've finished fading.
|
||||||
// and is 1/4 second if running at 60 frames per second although the actual value is not
|
if (opacity >= 255) {
|
||||||
// that important
|
mFadeOpacity = 255;
|
||||||
int opacity = mFadeOpacity + 255 / 15;
|
mFading = false;
|
||||||
// See if we've finished fading
|
}
|
||||||
if (opacity >= 255)
|
else {
|
||||||
{
|
mFadeOpacity = (unsigned char)opacity;
|
||||||
mFadeOpacity = 255;
|
}
|
||||||
mFading = false;
|
updateColors();
|
||||||
}
|
}
|
||||||
else
|
}
|
||||||
{
|
|
||||||
mFadeOpacity = (unsigned char)opacity;
|
|
||||||
}
|
|
||||||
updateColors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ImageComponent::hasImage()
|
bool ImageComponent::hasImage()
|
||||||
{
|
{
|
||||||
return (bool)mTexture;
|
return (bool)mTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, 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 ^ SIZE) | ((properties & (SIZE | POSITION)) ? ORIGIN : 0));
|
GuiComponent::applyTheme(theme, view, element, (properties ^ SIZE) |
|
||||||
|
((properties & (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((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
Vector2f scale = getParent() ? getParent()->getSize() :
|
||||||
|
Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||||
|
|
||||||
if(properties & ThemeFlags::SIZE)
|
if (properties & ThemeFlags::SIZE) {
|
||||||
{
|
if (elem->has("size"))
|
||||||
if(elem->has("size"))
|
setResize(elem->get<Vector2f>("size") * scale);
|
||||||
setResize(elem->get<Vector2f>("size") * scale);
|
else if (elem->has("maxSize"))
|
||||||
else if(elem->has("maxSize"))
|
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
|
||||||
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
|
else if (elem->has("minSize"))
|
||||||
else if(elem->has("minSize"))
|
setMinSize(elem->get<Vector2f>("minSize") * scale);
|
||||||
setMinSize(elem->get<Vector2f>("minSize") * scale);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(elem->has("default"))
|
if (elem->has("default"))
|
||||||
setDefaultImage(elem->get<std::string>("default"));
|
setDefaultImage(elem->get<std::string>("default"));
|
||||||
|
|
||||||
if(properties & PATH && elem->has("path"))
|
if (properties & PATH && elem->has("path")) {
|
||||||
{
|
bool tile = (elem->has("tile") && elem->get<bool>("tile"));
|
||||||
bool tile = (elem->has("tile") && elem->get<bool>("tile"));
|
setImage(elem->get<std::string>("path"), tile);
|
||||||
setImage(elem->get<std::string>("path"), tile);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(properties & COLOR)
|
if (properties & COLOR) {
|
||||||
{
|
if (elem->has("color"))
|
||||||
if(elem->has("color"))
|
setColorShift(elem->get<unsigned int>("color"));
|
||||||
setColorShift(elem->get<unsigned int>("color"));
|
if (elem->has("colorEnd"))
|
||||||
|
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
||||||
if (elem->has("colorEnd"))
|
if (elem->has("gradientType"))
|
||||||
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
|
setColorGradientHorizontal(!(elem->get<std::string>("gradientType").compare("horizontal")));
|
||||||
|
}
|
||||||
if (elem->has("gradientType"))
|
|
||||||
setColorGradientHorizontal(!(elem->get<std::string>("gradientType").compare("horizontal")));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> ImageComponent::getHelpPrompts()
|
std::vector<HelpPrompt> ImageComponent::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> ret;
|
std::vector<HelpPrompt> ret;
|
||||||
ret.push_back(HelpPrompt("a", "select"));
|
ret.push_back(HelpPrompt("a", "select"));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// ImageComponent.h
|
||||||
|
//
|
||||||
|
// Handles images: loading, resizing, cropping, color shifting etc.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -11,102 +17,108 @@ class TextureResource;
|
||||||
class ImageComponent : public GuiComponent
|
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);
|
||||||
|
|
||||||
//Loads the image at the given filepath. Will tile if tile is true (retrieves texture as tiling, creates vertices accordingly).
|
// Loads the image at the given filepath. Will tile if tile is true (retrieves texture
|
||||||
void setImage(std::string path, bool tile = false);
|
// as tiling, creates vertices accordingly).
|
||||||
//Loads an image from memory.
|
void setImage(std::string path, bool tile = false);
|
||||||
void setImage(const char* image, size_t length, bool tile = false);
|
// Loads an image from memory.
|
||||||
//Use an already existing texture.
|
void setImage(const char* image, size_t length, bool tile = false);
|
||||||
void setImage(const std::shared_ptr<TextureResource>& texture);
|
// Use an already existing texture.
|
||||||
|
void setImage(const std::shared_ptr<TextureResource>& texture);
|
||||||
|
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
|
|
||||||
// Resize the image to fit this size. If one axis is zero, scale that axis to maintain aspect ratio.
|
// Resize the image to fit this size. If one axis is zero, scale that axis to maintain
|
||||||
// If both are non-zero, potentially break the aspect ratio. If both are zero, no resizing.
|
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
||||||
// Can be set before or after an image is loaded.
|
// zero, don't do any resizing.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
// Can be set before or after an image is loaded.
|
||||||
void setResize(float width, float height);
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
void setResize(float width, float height);
|
||||||
|
inline 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()); }
|
inline 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()); }
|
inline void setMinSize(const Vector2f& size) { setMinSize(size.x(), size.y()); }
|
||||||
|
|
||||||
Vector2f getRotationSize() const override;
|
Vector2f getRotationSize() const override;
|
||||||
|
|
||||||
// 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.
|
||||||
void cropLeft(float percent);
|
void cropLeft(float percent);
|
||||||
void cropTop(float percent);
|
void cropTop(float percent);
|
||||||
void cropRight(float percent);
|
void cropRight(float percent);
|
||||||
void cropBot(float percent);
|
void cropBot(float percent);
|
||||||
void crop(float left, float top, float right, float bot);
|
void crop(float left, float top, float right, float bot);
|
||||||
void uncrop();
|
void uncrop();
|
||||||
|
|
||||||
// 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;
|
||||||
void setColorShiftEnd(unsigned int color);
|
void setColorShiftEnd(unsigned int color);
|
||||||
void setColorGradientHorizontal(bool horizontal);
|
void setColorGradientHorizontal(bool horizontal);
|
||||||
|
|
||||||
void setFlipX(bool flip); // Mirror on the X axis.
|
void setFlipX(bool flip); // Mirror on the X axis.
|
||||||
void setFlipY(bool flip); // Mirror on the Y axis.
|
void setFlipY(bool flip); // Mirror on the Y axis.
|
||||||
|
|
||||||
void setRotateByTargetSize(bool rotate); // 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);
|
||||||
|
|
||||||
// Returns the size of the current texture, or (0, 0) if none is loaded. May be different than drawn size (use getSize() for that).
|
// Returns the size of the current texture, or (0, 0) if none is loaded.
|
||||||
Vector2i getTextureSize() const;
|
// May be different than drawn size (use getSize() for that).
|
||||||
|
Vector2i getTextureSize() const;
|
||||||
|
|
||||||
Vector2f getSize() const override;
|
Vector2f getSize() const override;
|
||||||
|
|
||||||
bool hasImage();
|
bool hasImage();
|
||||||
|
|
||||||
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, const std::string& element, unsigned int properties) override;
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, 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; };
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> getTexture() { return mTexture; };
|
|
||||||
private:
|
private:
|
||||||
Vector2f mTargetSize;
|
Vector2f mTargetSize;
|
||||||
|
|
||||||
bool mFlipX, mFlipY, mTargetIsMax, mTargetIsMin;
|
bool mFlipX, mFlipY, mTargetIsMax, mTargetIsMin;
|
||||||
|
|
||||||
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
|
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
|
||||||
// Used internally whenever the resizing parameters or texture change.
|
// Used internally whenever the resizing parameters or texture change.
|
||||||
void resize();
|
void resize();
|
||||||
|
|
||||||
Renderer::Vertex mVertices[4];
|
Renderer::Vertex mVertices[4];
|
||||||
|
|
||||||
void updateVertices();
|
void updateVertices();
|
||||||
void updateColors();
|
void updateColors();
|
||||||
void fadeIn(bool textureLoaded);
|
void fadeIn(bool textureLoaded);
|
||||||
|
|
||||||
unsigned int mColorShift;
|
unsigned int mColorShift;
|
||||||
unsigned int mColorShiftEnd;
|
unsigned int mColorShiftEnd;
|
||||||
bool mColorGradientHorizontal;
|
bool mColorGradientHorizontal;
|
||||||
|
|
||||||
std::string mDefaultPath;
|
std::string mDefaultPath;
|
||||||
|
|
||||||
std::shared_ptr<TextureResource> mTexture;
|
std::shared_ptr<TextureResource> mTexture;
|
||||||
unsigned char mFadeOpacity;
|
unsigned char mFadeOpacity;
|
||||||
bool mFading;
|
bool mFading;
|
||||||
bool mForceLoad;
|
bool mForceLoad;
|
||||||
bool mDynamic;
|
bool mDynamic;
|
||||||
bool mRotateByTargetSize;
|
bool mRotateByTargetSize;
|
||||||
|
|
||||||
Vector2f mTopLeftCrop;
|
Vector2f mTopLeftCrop;
|
||||||
Vector2f mBottomRightCrop;
|
Vector2f mBottomRightCrop;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,168 +1,181 @@
|
||||||
|
//
|
||||||
|
// NinePatchComponent.cpp
|
||||||
|
//
|
||||||
|
// Breaks up an image into 3x3 patches to accomodate resizing without distortions.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/NinePatchComponent.h"
|
#include "components/NinePatchComponent.h"
|
||||||
|
|
||||||
#include "resources/TextureResource.h"
|
#include "resources/TextureResource.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "ThemeData.h"
|
#include "ThemeData.h"
|
||||||
|
|
||||||
NinePatchComponent::NinePatchComponent(Window* window, const std::string& path, unsigned int edgeColor, unsigned int centerColor) : GuiComponent(window),
|
NinePatchComponent::NinePatchComponent(
|
||||||
mCornerSize(16, 16),
|
Window* window,
|
||||||
mEdgeColor(edgeColor), mCenterColor(centerColor),
|
const std::string& path,
|
||||||
mPath(path),
|
unsigned int edgeColor,
|
||||||
mVertices(NULL)
|
unsigned int centerColor)
|
||||||
|
: GuiComponent(window),
|
||||||
|
mCornerSize(16, 16),
|
||||||
|
mEdgeColor(edgeColor),
|
||||||
|
mCenterColor(centerColor),
|
||||||
|
mPath(path),
|
||||||
|
mVertices(nullptr)
|
||||||
{
|
{
|
||||||
if(!mPath.empty())
|
if (!mPath.empty())
|
||||||
buildVertices();
|
buildVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
NinePatchComponent::~NinePatchComponent()
|
NinePatchComponent::~NinePatchComponent()
|
||||||
{
|
{
|
||||||
if (mVertices != NULL)
|
if (mVertices != nullptr)
|
||||||
delete[] mVertices;
|
delete[] mVertices;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::updateColors()
|
void NinePatchComponent::updateColors()
|
||||||
{
|
{
|
||||||
const unsigned int edgeColor = Renderer::convertColor(mEdgeColor);
|
const unsigned int edgeColor = Renderer::convertColor(mEdgeColor);
|
||||||
const unsigned int centerColor = Renderer::convertColor(mCenterColor);
|
const unsigned int centerColor = Renderer::convertColor(mCenterColor);
|
||||||
|
|
||||||
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()
|
||||||
{
|
{
|
||||||
if(mVertices != NULL)
|
if (mVertices != nullptr)
|
||||||
delete[] mVertices;
|
delete[] mVertices;
|
||||||
|
|
||||||
mTexture = TextureResource::get(mPath);
|
mTexture = TextureResource::get(mPath);
|
||||||
|
|
||||||
if(mTexture->getSize() == Vector2i::Zero())
|
if (mTexture->getSize() == Vector2i::Zero()) {
|
||||||
{
|
mVertices = nullptr;
|
||||||
mVertices = NULL;
|
LOG(LogWarning) << "NinePatchComponent missing texture!";
|
||||||
LOG(LogWarning) << "NinePatchComponent missing texture!";
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
mVertices = new Renderer::Vertex[6 * 9];
|
mVertices = new Renderer::Vertex[6 * 9];
|
||||||
|
|
||||||
const Vector2f texSize = Vector2f((float)mTexture->getSize().x(), (float)mTexture->getSize().y());
|
const Vector2f texSize = Vector2f((float)mTexture->getSize().x(),
|
||||||
|
(float)mTexture->getSize().y());
|
||||||
|
|
||||||
const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2, mCornerSize.x()};
|
const float imgSizeX[3] = { mCornerSize.x(), mSize.x() - mCornerSize.x() * 2, mCornerSize.x()};
|
||||||
const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2, mCornerSize.y()};
|
const float imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2, mCornerSize.y()};
|
||||||
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]};
|
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]};
|
||||||
const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[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 left corner origin vs. verticies having a top left origin
|
// The "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom
|
||||||
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2) / texSize.x(), mCornerSize.x() / texSize.x() };
|
// left corner origin vs. verticies having a top left origin.
|
||||||
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2) / texSize.y(), -mCornerSize.y() / texSize.y() };
|
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2) / texSize.x(), mCornerSize.x() / texSize.x() };
|
||||||
const float texPosX[3] = { 0, texSizeX[0], texSizeX[0] + texSizeX[1] };
|
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2) / texSize.y(), -mCornerSize.y() / texSize.y() };
|
||||||
const float texPosY[3] = { 1, 1 + texSizeY[0], 1 + texSizeY[0] + texSizeY[1] };
|
const float texPosX[3] = { 0, texSizeX[0], texSizeX[0] + texSizeX[1] };
|
||||||
|
const float texPosY[3] = { 1, 1 + texSizeY[0], 1 + texSizeY[0] + texSizeY[1] };
|
||||||
|
|
||||||
int v = 0;
|
int v = 0;
|
||||||
for(int slice = 0; slice < 9; slice++)
|
|
||||||
{
|
|
||||||
const int sliceX = slice % 3;
|
|
||||||
const int sliceY = slice / 3;
|
|
||||||
const Vector2f imgPos = Vector2f(imgPosX[sliceX], imgPosY[sliceY]);
|
|
||||||
const Vector2f imgSize = Vector2f(imgSizeX[sliceX], imgSizeY[sliceY]);
|
|
||||||
const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]);
|
|
||||||
const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]);
|
|
||||||
|
|
||||||
mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 };
|
for (int slice = 0; slice < 9; slice++) {
|
||||||
mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 };
|
const int sliceX = slice % 3;
|
||||||
mVertices[v + 3] = { { imgPos.x() + imgSize.x(), imgPos.y() }, { texPos.x() + texSize.x(), texPos.y() }, 0 };
|
const int sliceY = slice / 3;
|
||||||
mVertices[v + 4] = { { imgPos.x() + imgSize.x(), imgPos.y() + imgSize.y() }, { texPos.x() + texSize.x(), texPos.y() + texSize.y() }, 0 };
|
const Vector2f imgPos = Vector2f(imgPosX[sliceX], imgPosY[sliceY]);
|
||||||
|
const Vector2f imgSize = Vector2f(imgSizeX[sliceX], imgSizeY[sliceY]);
|
||||||
|
const Vector2f texPos = Vector2f(texPosX[sliceX], texPosY[sliceY]);
|
||||||
|
const Vector2f texSize = Vector2f(texSizeX[sliceX], texSizeY[sliceY]);
|
||||||
|
|
||||||
// round vertices
|
mVertices[v + 1] = { { imgPos.x() , imgPos.y() }, { texPos.x(), texPos.y() }, 0 };
|
||||||
for(int i = 1; i < 5; ++i)
|
mVertices[v + 2] = { { imgPos.x() , imgPos.y() + imgSize.y() }, { texPos.x(), texPos.y() + texSize.y() }, 0 };
|
||||||
mVertices[v + i].pos.round();
|
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 };
|
||||||
|
|
||||||
// make duplicates of first and last vertex so this can be rendered as a triangle strip
|
// Round vertices.
|
||||||
mVertices[v + 0] = mVertices[v + 1];
|
for (int i = 1; i < 5; ++i)
|
||||||
mVertices[v + 5] = mVertices[v + 4];
|
mVertices[v + i].pos.round();
|
||||||
|
|
||||||
v += 6;
|
// Make duplicates of first and last vertex so this can be rendered as a triangle strip.
|
||||||
}
|
mVertices[v + 0] = mVertices[v + 1];
|
||||||
|
mVertices[v + 5] = mVertices[v + 4];
|
||||||
|
|
||||||
updateColors();
|
v += 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::render(const Transform4x4f& parentTrans)
|
void NinePatchComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
if(mTexture && mVertices != NULL)
|
if (mTexture && mVertices != nullptr) {
|
||||||
{
|
Renderer::setMatrix(trans);
|
||||||
Renderer::setMatrix(trans);
|
|
||||||
|
|
||||||
mTexture->bind();
|
mTexture->bind();
|
||||||
Renderer::drawTriangleStrips(&mVertices[0], 6*9);
|
Renderer::drawTriangleStrips(&mVertices[0], 6*9);
|
||||||
}
|
}
|
||||||
|
|
||||||
renderChildren(trans);
|
renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::onSizeChanged()
|
void NinePatchComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
buildVertices();
|
buildVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
const Vector2f& NinePatchComponent::getCornerSize() const
|
const Vector2f& NinePatchComponent::getCornerSize() const
|
||||||
{
|
{
|
||||||
return mCornerSize;
|
return mCornerSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::setCornerSize(int sizeX, int sizeY)
|
void NinePatchComponent::setCornerSize(int sizeX, int sizeY)
|
||||||
{
|
{
|
||||||
mCornerSize = Vector2f(sizeX, sizeY);
|
mCornerSize = Vector2f(sizeX, sizeY);
|
||||||
buildVertices();
|
buildVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding)
|
void NinePatchComponent::fitTo(Vector2f size, Vector3f position, Vector2f padding)
|
||||||
{
|
{
|
||||||
size += padding;
|
size += padding;
|
||||||
position[0] -= padding.x() / 2;
|
position[0] -= padding.x() / 2;
|
||||||
position[1] -= padding.y() / 2;
|
position[1] -= padding.y() / 2;
|
||||||
|
|
||||||
setSize(size + mCornerSize * 2);
|
setSize(size + mCornerSize * 2);
|
||||||
setPosition(position.x() + Math::lerp(-mCornerSize.x(), mCornerSize.x(), mOrigin.x()),
|
setPosition(position.x() + Math::lerp(-mCornerSize.x(), mCornerSize.x(), mOrigin.x()),
|
||||||
position.y() + Math::lerp(-mCornerSize.y(), mCornerSize.y(), mOrigin.y()));
|
position.y() + Math::lerp(-mCornerSize.y(), mCornerSize.y(), mOrigin.y()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::setImagePath(const std::string& path)
|
void NinePatchComponent::setImagePath(const std::string& path)
|
||||||
{
|
{
|
||||||
mPath = path;
|
mPath = path;
|
||||||
buildVertices();
|
buildVertices();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::setEdgeColor(unsigned int edgeColor)
|
void NinePatchComponent::setEdgeColor(unsigned int edgeColor)
|
||||||
{
|
{
|
||||||
mEdgeColor = edgeColor;
|
mEdgeColor = edgeColor;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::setCenterColor(unsigned int centerColor)
|
void NinePatchComponent::setCenterColor(unsigned int centerColor)
|
||||||
{
|
{
|
||||||
mCenterColor = centerColor;
|
mCenterColor = centerColor;
|
||||||
updateColors();
|
updateColors();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NinePatchComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
void NinePatchComponent::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);
|
||||||
|
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "ninepatch");
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "ninepatch");
|
if (!elem)
|
||||||
if(!elem)
|
return;
|
||||||
return;
|
|
||||||
|
|
||||||
if(properties & PATH && elem->has("path"))
|
if (properties & PATH && elem->has("path"))
|
||||||
setImagePath(elem->get<std::string>("path"));
|
setImagePath(elem->get<std::string>("path"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// NinePatchComponent.h
|
||||||
|
//
|
||||||
|
// Breaks up an image into 3x3 patches to accomodate resizing without distortions.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -7,13 +13,14 @@
|
||||||
|
|
||||||
class TextureResource;
|
class TextureResource;
|
||||||
|
|
||||||
// Display an image in a way so that edges don't get too distorted no matter the final size. Useful for UI elements like backgrounds, buttons, etc.
|
// Display an image in a way so that edges don't get too distorted no matter the final size.
|
||||||
|
// Useful for UI elements like backgrounds, buttons, etc.
|
||||||
// This is accomplished by splitting an image into 9 pieces:
|
// This is accomplished by splitting an image into 9 pieces:
|
||||||
// ___________
|
// ___________
|
||||||
// |_1_|_2_|_3_|
|
// |_1_|_2_|_3_|
|
||||||
// |_4_|_5_|_6_|
|
// |_4_|_5_|_6_|
|
||||||
// |_7_|_8_|_9_|
|
// |_7_|_8_|_9_|
|
||||||
|
//
|
||||||
// Corners (1, 3, 7, 9) will not be stretched at all.
|
// Corners (1, 3, 7, 9) will not be stretched at all.
|
||||||
// Borders (2, 4, 6, 8) will be stretched along one axis (2 and 8 horizontally, 4 and 6 vertically).
|
// Borders (2, 4, 6, 8) will be stretched along one axis (2 and 8 horizontally, 4 and 6 vertically).
|
||||||
// The center (5) will be stretched.
|
// The center (5) will be stretched.
|
||||||
|
@ -21,36 +28,41 @@ class TextureResource;
|
||||||
class NinePatchComponent : public GuiComponent
|
class NinePatchComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
NinePatchComponent(Window* window, const std::string& path = "", unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF);
|
NinePatchComponent(Window* window, const std::string& path = "",
|
||||||
virtual ~NinePatchComponent();
|
unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF);
|
||||||
|
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(), Vector2f padding = Vector2f::Zero());
|
void fitTo(Vector2f size, Vector3f position = Vector3f::Zero(),
|
||||||
|
Vector2f padding = Vector2f::Zero());
|
||||||
|
|
||||||
void setImagePath(const std::string& path);
|
void setImagePath(const std::string& path);
|
||||||
void setEdgeColor(unsigned int edgeColor); // Apply a color shift to the "edge" parts of the ninepatch.
|
// Apply a color shift to the "edge" parts of the ninepatch.
|
||||||
void setCenterColor(unsigned int centerColor); // Apply a color shift to the "center" part of the ninepatch.
|
void setEdgeColor(unsigned int edgeColor);
|
||||||
|
// Apply a color shift to the "center" part of the ninepatch.
|
||||||
|
void setCenterColor(unsigned int centerColor);
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
||||||
|
const std::string& element, unsigned int properties) override;
|
||||||
|
|
||||||
const Vector2f& getCornerSize() const;
|
const Vector2f& getCornerSize() const;
|
||||||
void setCornerSize(int sizeX, int sizeY);
|
void setCornerSize(int sizeX, int sizeY);
|
||||||
inline void setCornerSize(const Vector2f& size) { setCornerSize(size.x(), size.y()); }
|
inline void setCornerSize(const Vector2f& size) { setCornerSize(size.x(), size.y()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void buildVertices();
|
void buildVertices();
|
||||||
void updateColors();
|
void updateColors();
|
||||||
|
|
||||||
Renderer::Vertex* mVertices;
|
Renderer::Vertex* mVertices;
|
||||||
|
|
||||||
std::string mPath;
|
std::string mPath;
|
||||||
Vector2f mCornerSize;
|
Vector2f mCornerSize;
|
||||||
unsigned int mEdgeColor;
|
unsigned int mEdgeColor;
|
||||||
unsigned int mCenterColor;
|
unsigned int mCenterColor;
|
||||||
std::shared_ptr<TextureResource> mTexture;
|
std::shared_ptr<TextureResource> mTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
//
|
//
|
||||||
// OptionListComponent.h
|
// OptionListComponent.h
|
||||||
//
|
//
|
||||||
// Provides a list of options.
|
// Provides a list of options.
|
||||||
// Supports various types using templates.
|
// Supports various types using templates.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -28,354 +28,354 @@ template<typename T>
|
||||||
class OptionListComponent : public GuiComponent
|
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);
|
||||||
mText.setColor(0x777777FF);
|
mText.setColor(0x777777FF);
|
||||||
mText.setHorizontalAlignment(ALIGN_CENTER);
|
mText.setHorizontalAlignment(ALIGN_CENTER);
|
||||||
addChild(&mText);
|
addChild(&mText);
|
||||||
|
|
||||||
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||||
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||||
|
|
||||||
if(mMultiSelect) {
|
if(mMultiSelect) {
|
||||||
mRightArrow.setImage(":/graphics/arrow.svg");
|
mRightArrow.setImage(":/graphics/arrow.svg");
|
||||||
addChild(&mRightArrow);
|
addChild(&mRightArrow);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mLeftArrow.setImage(":/graphics/option_arrow.svg");
|
mLeftArrow.setImage(":/graphics/option_arrow.svg");
|
||||||
mLeftArrow.setFlipX(true);
|
mLeftArrow.setFlipX(true);
|
||||||
addChild(&mLeftArrow);
|
addChild(&mLeftArrow);
|
||||||
|
|
||||||
mRightArrow.setImage(":/graphics/option_arrow.svg");
|
mRightArrow.setImage(":/graphics/option_arrow.svg");
|
||||||
addChild(&mRightArrow);
|
addChild(&mRightArrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize(mLeftArrow.getSize().x() + mRightArrow.getSize().x(), font->getHeight());
|
setSize(mLeftArrow.getSize().x() + mRightArrow.getSize().x(), font->getHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles positioning/resizing of text and arrows.
|
// Handles positioning/resizing of text and arrows.
|
||||||
void onSizeChanged() override
|
void onSizeChanged() override
|
||||||
{
|
{
|
||||||
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
mLeftArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||||
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
mRightArrow.setResize(0, mText.getFont()->getLetterHeight());
|
||||||
|
|
||||||
if(mSize.x() < (mLeftArrow.getSize().x() + mRightArrow.getSize().x())) {
|
if(mSize.x() < (mLeftArrow.getSize().x() + mRightArrow.getSize().x())) {
|
||||||
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(), mText.getFont()->getHeight());
|
mRightArrow.getSize().x(), 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
|
||||||
{
|
{
|
||||||
if(input.value != 0) {
|
if(input.value != 0) {
|
||||||
if(config->isMappedTo("a", input)) {
|
if(config->isMappedTo("a", input)) {
|
||||||
open();
|
open();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(!mMultiSelect) {
|
if(!mMultiSelect) {
|
||||||
if(config->isMappedLike("left", input)) {
|
if(config->isMappedLike("left", input)) {
|
||||||
// Move selection to previous.
|
// Move selection to previous.
|
||||||
unsigned int i = getSelectedId();
|
unsigned int i = getSelectedId();
|
||||||
int next = (int)i - 1;
|
int next = (int)i - 1;
|
||||||
if(next < 0)
|
if(next < 0)
|
||||||
next += (int)mEntries.size();
|
next += (int)mEntries.size();
|
||||||
|
|
||||||
mEntries.at(i).selected = false;
|
mEntries.at(i).selected = false;
|
||||||
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)) {
|
||||||
// Move selection to next.
|
// Move selection to next.
|
||||||
unsigned int i = getSelectedId();
|
unsigned int i = getSelectedId();
|
||||||
int next = (i + 1) % mEntries.size();
|
int next = (i + 1) % mEntries.size();
|
||||||
mEntries.at(i).selected = false;
|
mEntries.at(i).selected = false;
|
||||||
mEntries.at(next).selected = true;
|
mEntries.at(next).selected = true;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<T> getSelectedObjects()
|
std::vector<T> getSelectedObjects()
|
||||||
{
|
{
|
||||||
std::vector<T> ret;
|
std::vector<T> ret;
|
||||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
||||||
if(it->selected)
|
if(it->selected)
|
||||||
ret.push_back(it->object);
|
ret.push_back(it->object);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
T getSelected()
|
T getSelected()
|
||||||
{
|
{
|
||||||
assert(mMultiSelect == false);
|
assert(mMultiSelect == false);
|
||||||
auto selected = getSelectedObjects();
|
auto selected = getSelectedObjects();
|
||||||
assert(selected.size() == 1);
|
assert(selected.size() == 1);
|
||||||
return selected.at(0);
|
return selected.at(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(const std::string& name, const T& obj, bool selected)
|
void add(const std::string& name, const T& obj, bool selected)
|
||||||
{
|
{
|
||||||
OptionListData e;
|
OptionListData e;
|
||||||
e.name = name;
|
e.name = name;
|
||||||
e.object = obj;
|
e.object = obj;
|
||||||
e.selected = selected;
|
e.selected = selected;
|
||||||
|
|
||||||
mEntries.push_back(e);
|
mEntries.push_back(e);
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool selectEntry(unsigned int entry)
|
bool selectEntry(unsigned int entry)
|
||||||
{
|
{
|
||||||
if (entry > mEntries.size()) {
|
if (entry > mEntries.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mEntries.at(entry).selected = true;
|
mEntries.at(entry).selected = true;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool unselectEntry(unsigned int entry)
|
bool unselectEntry(unsigned int entry)
|
||||||
{
|
{
|
||||||
if (entry > mEntries.size()) {
|
if (entry > mEntries.size()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mEntries.at(entry).selected = false;
|
mEntries.at(entry).selected = false;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectAll()
|
void selectAll()
|
||||||
{
|
{
|
||||||
for(unsigned int i = 0; i < mEntries.size(); i++)
|
for(unsigned int i = 0; i < mEntries.size(); i++)
|
||||||
mEntries.at(i).selected = true;
|
mEntries.at(i).selected = true;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void selectNone()
|
void selectNone()
|
||||||
{
|
{
|
||||||
for(unsigned int i = 0; i < mEntries.size(); i++)
|
for(unsigned int i = 0; i < mEntries.size(); i++)
|
||||||
mEntries.at(i).selected = false;
|
mEntries.at(i).selected = false;
|
||||||
onSelectedChanged();
|
onSelectedChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct OptionListData {
|
struct OptionListData {
|
||||||
std::string name;
|
std::string name;
|
||||||
T object;
|
T object;
|
||||||
bool selected;
|
bool selected;
|
||||||
};
|
};
|
||||||
|
|
||||||
HelpStyle mHelpStyle;
|
HelpStyle mHelpStyle;
|
||||||
|
|
||||||
unsigned int getSelectedId()
|
unsigned int getSelectedId()
|
||||||
{
|
{
|
||||||
assert(mMultiSelect == false);
|
assert(mMultiSelect == false);
|
||||||
for(unsigned int i = 0; i < mEntries.size(); i++) {
|
for(unsigned int i = 0; i < mEntries.size(); i++) {
|
||||||
if(mEntries.at(i).selected)
|
if(mEntries.at(i).selected)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
void open()
|
void open()
|
||||||
{
|
{
|
||||||
mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName));
|
mWindow->pushGui(new OptionListPopup(mWindow, getHelpStyle(), this, mName));
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSelectedChanged()
|
void onSelectedChanged()
|
||||||
{
|
{
|
||||||
if(mMultiSelect) {
|
if(mMultiSelect) {
|
||||||
// Display # selected.
|
// Display # selected.
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
ss << getSelectedObjects().size() << " SELECTED";
|
ss << getSelectedObjects().size() << " SELECTED";
|
||||||
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() + 24, mText.getSize().y());
|
setSize(mText.getSize().x() + mRightArrow.getSize().x() + 24, mText.getSize().y());
|
||||||
if(mParent) // Hack since theres no "on child size changed" callback atm...
|
if(mParent) // Hack since theres no "on child size changed" callback atm...
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Display currently selected + l/r cursors.
|
// Display currently selected + l/r cursors.
|
||||||
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
|
||||||
if(it->selected) {
|
if(it->selected) {
|
||||||
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() + 24, mText.getSize().y());
|
mRightArrow.getSize().x() + 24, mText.getSize().y());
|
||||||
if(mParent) // Hack since theres no "on child size changed" callback atm...
|
if(mParent) // Hack since theres no "on child size changed" callback atm...
|
||||||
mParent->onSizeChanged();
|
mParent->onSizeChanged();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override
|
std::vector<HelpPrompt> getHelpPrompts() override
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts;
|
std::vector<HelpPrompt> prompts;
|
||||||
if(!mMultiSelect)
|
if(!mMultiSelect)
|
||||||
prompts.push_back(HelpPrompt("left/right", "change value"));
|
prompts.push_back(HelpPrompt("left/right", "change value"));
|
||||||
|
|
||||||
prompts.push_back(HelpPrompt("a", "select"));
|
prompts.push_back(HelpPrompt("a", "select"));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool mMultiSelect;
|
bool mMultiSelect;
|
||||||
|
|
||||||
std::string mName;
|
std::string mName;
|
||||||
TextComponent mText;
|
TextComponent mText;
|
||||||
ImageComponent mLeftArrow;
|
ImageComponent mLeftArrow;
|
||||||
ImageComponent mRightArrow;
|
ImageComponent mRightArrow;
|
||||||
|
|
||||||
std::vector<OptionListData> mEntries;
|
std::vector<OptionListData> mEntries;
|
||||||
|
|
||||||
// Subclass to OptionListComponent.
|
// Subclass to OptionListComponent.
|
||||||
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;
|
||||||
|
|
||||||
// For select all/none.
|
// For select all/none.
|
||||||
std::vector<ImageComponent*> checkboxes;
|
std::vector<ImageComponent*> checkboxes;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
if(mParent->mMultiSelect) {
|
if(mParent->mMultiSelect) {
|
||||||
// Add checkbox.
|
// Add checkbox.
|
||||||
auto checkbox = std::make_shared<ImageComponent>(mWindow);
|
auto checkbox = std::make_shared<ImageComponent>(mWindow);
|
||||||
checkbox->setImage(it->selected ? CHECKED_PATH : UNCHECKED_PATH);
|
checkbox->setImage(it->selected ? CHECKED_PATH : UNCHECKED_PATH);
|
||||||
checkbox->setResize(0, font->getLetterHeight());
|
checkbox->setResize(0, font->getLetterHeight());
|
||||||
row.addElement(checkbox, false);
|
row.addElement(checkbox, false);
|
||||||
|
|
||||||
// Input handler.
|
// Input handler.
|
||||||
// Update checkbox state & selected value.
|
// Update checkbox state & selected value.
|
||||||
row.makeAcceptInputHandler([this, &e, checkbox] {
|
row.makeAcceptInputHandler([this, &e, checkbox] {
|
||||||
e.selected = !e.selected;
|
e.selected = !e.selected;
|
||||||
checkbox->setImage(e.selected ? CHECKED_PATH : UNCHECKED_PATH);
|
checkbox->setImage(e.selected ? CHECKED_PATH : UNCHECKED_PATH);
|
||||||
mParent->onSelectedChanged();
|
mParent->onSelectedChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
// For select all/none.
|
// For select all/none.
|
||||||
checkboxes.push_back(checkbox.get());
|
checkboxes.push_back(checkbox.get());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// Input handler for non-multiselect.
|
// Input handler for non-multiselect.
|
||||||
// Update selected value and close.
|
// Update selected value and close.
|
||||||
row.makeAcceptInputHandler([this, &e] {
|
row.makeAcceptInputHandler([this, &e] {
|
||||||
mParent->mEntries.at(mParent->getSelectedId()).selected = false;
|
mParent->mEntries.at(mParent->getSelectedId()).selected = false;
|
||||||
e.selected = true;
|
e.selected = true;
|
||||||
mParent->onSelectedChanged();
|
mParent->onSelectedChanged();
|
||||||
delete this;
|
delete this;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also set cursor to this row if we're not multi-select and this row is selected.
|
// Also set cursor to this row if we're not multi-select and this row is selected.
|
||||||
mMenu.addRow(row, (!mParent->mMultiSelect && it->selected));
|
mMenu.addRow(row, (!mParent->mMultiSelect && it->selected));
|
||||||
}
|
}
|
||||||
|
|
||||||
mMenu.addButton("BACK", "back", [this] { delete this; });
|
mMenu.addButton("BACK", "back", [this] { delete this; });
|
||||||
|
|
||||||
if(mParent->mMultiSelect) {
|
if(mParent->mMultiSelect) {
|
||||||
mMenu.addButton("SELECT ALL", "select all", [this, checkboxes] {
|
mMenu.addButton("SELECT ALL", "select all", [this, checkboxes] {
|
||||||
for(unsigned int i = 0; i < mParent->mEntries.size(); i++) {
|
for(unsigned int i = 0; i < mParent->mEntries.size(); i++) {
|
||||||
mParent->mEntries.at(i).selected = true;
|
mParent->mEntries.at(i).selected = true;
|
||||||
checkboxes.at(i)->setImage(CHECKED_PATH);
|
checkboxes.at(i)->setImage(CHECKED_PATH);
|
||||||
}
|
}
|
||||||
mParent->onSelectedChanged();
|
mParent->onSelectedChanged();
|
||||||
});
|
});
|
||||||
|
|
||||||
mMenu.addButton("SELECT NONE", "select none", [this, checkboxes] {
|
mMenu.addButton("SELECT NONE", "select none", [this, checkboxes] {
|
||||||
for(unsigned int i = 0; i < mParent->mEntries.size(); i++) {
|
for(unsigned int i = 0; i < mParent->mEntries.size(); i++) {
|
||||||
mParent->mEntries.at(i).selected = false;
|
mParent->mEntries.at(i).selected = false;
|
||||||
checkboxes.at(i)->setImage(UNCHECKED_PATH);
|
checkboxes.at(i)->setImage(UNCHECKED_PATH);
|
||||||
}
|
}
|
||||||
mParent->onSelectedChanged();
|
mParent->onSelectedChanged();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2,
|
mMenu.setPosition((Renderer::getScreenWidth() - mMenu.getSize().x()) / 2,
|
||||||
Renderer::getScreenHeight() * 0.15f);
|
Renderer::getScreenHeight() * 0.15f);
|
||||||
addChild(&mMenu);
|
addChild(&mMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool input(InputConfig* config, Input input) override
|
bool input(InputConfig* config, Input input) override
|
||||||
{
|
{
|
||||||
if(config->isMappedTo("b", input) && input.value != 0) {
|
if(config->isMappedTo("b", input) && input.value != 0) {
|
||||||
delete this;
|
delete this;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> getHelpPrompts() override
|
std::vector<HelpPrompt> getHelpPrompts() override
|
||||||
{
|
{
|
||||||
auto prompts = mMenu.getHelpPrompts();
|
auto prompts = mMenu.getHelpPrompts();
|
||||||
prompts.push_back(HelpPrompt("a", "select"));
|
prompts.push_back(HelpPrompt("a", "select"));
|
||||||
prompts.push_back(HelpPrompt("b", "back"));
|
prompts.push_back(HelpPrompt("b", "back"));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
||||||
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
HelpStyle getHelpStyle() override { return mHelpStyle; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MenuComponent mMenu;
|
MenuComponent mMenu;
|
||||||
OptionListComponent<T>* mParent;
|
OptionListComponent<T>* mParent;
|
||||||
HelpStyle mHelpStyle;
|
HelpStyle mHelpStyle;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_OPTION_LIST_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_OPTION_LIST_COMPONENT_H
|
||||||
|
|
|
@ -1,133 +1,141 @@
|
||||||
|
//
|
||||||
|
// ScrollableContainer.cpp
|
||||||
|
//
|
||||||
|
// Area containing scrollable information, for example the game description
|
||||||
|
// text container in the detailed, video and grid views.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/ScrollableContainer.h"
|
#include "components/ScrollableContainer.h"
|
||||||
|
|
||||||
#include "math/Vector2i.h"
|
#include "math/Vector2i.h"
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
|
|
||||||
#define AUTO_SCROLL_RESET_DELAY 3000 // ms to reset to top after we reach the bottom
|
#define AUTO_SCROLL_RESET_DELAY 3000 // ms to reset to top after we reach the bottom.
|
||||||
#define AUTO_SCROLL_DELAY 1000 // ms to wait before we start to scroll
|
#define AUTO_SCROLL_DELAY 1000 // ms to wait before we start to scroll.
|
||||||
#define AUTO_SCROLL_SPEED 50 // ms between scrolls
|
#define AUTO_SCROLL_SPEED 100 // ms between scrolls.
|
||||||
|
|
||||||
ScrollableContainer::ScrollableContainer(Window* window) : GuiComponent(window),
|
ScrollableContainer::ScrollableContainer(
|
||||||
mAutoScrollDelay(0), mAutoScrollSpeed(0), mAutoScrollAccumulator(0), mScrollPos(0, 0), mScrollDir(0, 0), mAutoScrollResetAccumulator(0)
|
Window* window)
|
||||||
|
: GuiComponent(window),
|
||||||
|
mAutoScrollDelay(0),
|
||||||
|
mAutoScrollSpeed(0),
|
||||||
|
mAutoScrollAccumulator(0),
|
||||||
|
mScrollPos(0, 0),
|
||||||
|
mScrollDir(0, 0),
|
||||||
|
mAutoScrollResetAccumulator(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::render(const Transform4x4f& parentTrans)
|
void ScrollableContainer::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y());
|
Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y());
|
||||||
|
|
||||||
Vector3f dimScaled = trans * Vector3f(mSize.x(), mSize.y(), 0);
|
Vector3f dimScaled = trans * Vector3f(mSize.x(), mSize.y(), 0);
|
||||||
Vector2i clipDim((int)(dimScaled.x() - trans.translation().x()), (int)(dimScaled.y() - trans.translation().y()));
|
Vector2i clipDim((int)(dimScaled.x() - trans.translation().x()),
|
||||||
|
(int)(dimScaled.y() - trans.translation().y()));
|
||||||
|
|
||||||
Renderer::pushClipRect(clipPos, clipDim);
|
Renderer::pushClipRect(clipPos, clipDim);
|
||||||
|
|
||||||
trans.translate(-Vector3f(mScrollPos.x(), mScrollPos.y(), 0));
|
trans.translate(-Vector3f(mScrollPos.x(), mScrollPos.y(), 0));
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
|
Renderer::popClipRect();
|
||||||
Renderer::popClipRect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::setAutoScroll(bool autoScroll)
|
void ScrollableContainer::setAutoScroll(bool autoScroll)
|
||||||
{
|
{
|
||||||
if(autoScroll)
|
if (autoScroll) {
|
||||||
{
|
mScrollDir = Vector2f(0, 1);
|
||||||
mScrollDir = Vector2f(0, 1);
|
mAutoScrollDelay = AUTO_SCROLL_DELAY;
|
||||||
mAutoScrollDelay = AUTO_SCROLL_DELAY;
|
mAutoScrollSpeed = AUTO_SCROLL_SPEED;
|
||||||
mAutoScrollSpeed = AUTO_SCROLL_SPEED;
|
reset();
|
||||||
reset();
|
}
|
||||||
}else{
|
else {
|
||||||
mScrollDir = Vector2f(0, 0);
|
mScrollDir = Vector2f(0, 0);
|
||||||
mAutoScrollDelay = 0;
|
mAutoScrollDelay = 0;
|
||||||
mAutoScrollSpeed = 0;
|
mAutoScrollSpeed = 0;
|
||||||
mAutoScrollAccumulator = 0;
|
mAutoScrollAccumulator = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f ScrollableContainer::getScrollPos() const
|
Vector2f ScrollableContainer::getScrollPos() const
|
||||||
{
|
{
|
||||||
return mScrollPos;
|
return mScrollPos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::setScrollPos(const Vector2f& pos)
|
void ScrollableContainer::setScrollPos(const Vector2f& pos)
|
||||||
{
|
{
|
||||||
mScrollPos = pos;
|
mScrollPos = pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::update(int deltaTime)
|
void ScrollableContainer::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if(mAutoScrollSpeed != 0)
|
if (mAutoScrollSpeed != 0) {
|
||||||
{
|
mAutoScrollAccumulator += deltaTime;
|
||||||
mAutoScrollAccumulator += deltaTime;
|
|
||||||
|
|
||||||
//scale speed by our width! more text per line = slower scrolling
|
// Scale speed by our width! more text per line = slower scrolling.
|
||||||
const float widthMod = (680.0f / getSize().x());
|
const float widthMod = (680.0f / getSize().x());
|
||||||
while(mAutoScrollAccumulator >= mAutoScrollSpeed)
|
while (mAutoScrollAccumulator >= mAutoScrollSpeed) {
|
||||||
{
|
mScrollPos += mScrollDir;
|
||||||
mScrollPos += mScrollDir;
|
mAutoScrollAccumulator -= mAutoScrollSpeed;
|
||||||
mAutoScrollAccumulator -= mAutoScrollSpeed;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
//clip scrolling within bounds
|
// Clip scrolling within bounds.
|
||||||
if(mScrollPos.x() < 0)
|
if (mScrollPos.x() < 0)
|
||||||
mScrollPos[0] = 0;
|
mScrollPos[0] = 0;
|
||||||
if(mScrollPos.y() < 0)
|
if (mScrollPos.y() < 0)
|
||||||
mScrollPos[1] = 0;
|
mScrollPos[1] = 0;
|
||||||
|
|
||||||
const Vector2f contentSize = getContentSize();
|
const Vector2f contentSize = getContentSize();
|
||||||
if(mScrollPos.x() + getSize().x() > contentSize.x())
|
if (mScrollPos.x() + getSize().x() > contentSize.x()) {
|
||||||
{
|
mScrollPos[0] = contentSize.x() - getSize().x();
|
||||||
mScrollPos[0] = contentSize.x() - getSize().x();
|
mAtEnd = true;
|
||||||
mAtEnd = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(contentSize.y() < getSize().y())
|
if (contentSize.y() < getSize().y()) {
|
||||||
{
|
mScrollPos[1] = 0;
|
||||||
mScrollPos[1] = 0;
|
}
|
||||||
}else if(mScrollPos.y() + getSize().y() > contentSize.y())
|
else if (mScrollPos.y() + getSize().y() > contentSize.y()) {
|
||||||
{
|
mScrollPos[1] = contentSize.y() - getSize().y();
|
||||||
mScrollPos[1] = contentSize.y() - getSize().y();
|
mAtEnd = true;
|
||||||
mAtEnd = true;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(mAtEnd)
|
if (mAtEnd) {
|
||||||
{
|
mAutoScrollResetAccumulator += deltaTime;
|
||||||
mAutoScrollResetAccumulator += deltaTime;
|
if (mAutoScrollResetAccumulator >= AUTO_SCROLL_RESET_DELAY)
|
||||||
if(mAutoScrollResetAccumulator >= AUTO_SCROLL_RESET_DELAY)
|
reset();
|
||||||
reset();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
GuiComponent::update(deltaTime);
|
GuiComponent::update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
//this should probably return a box to allow for when controls don't start at 0,0
|
// This should probably return a box to allow for when controls don't start at 0,0.
|
||||||
Vector2f ScrollableContainer::getContentSize()
|
Vector2f ScrollableContainer::getContentSize()
|
||||||
{
|
{
|
||||||
Vector2f max(0, 0);
|
Vector2f max(0, 0);
|
||||||
for(unsigned int i = 0; i < mChildren.size(); i++)
|
for (unsigned int i = 0; i < mChildren.size(); i++) {
|
||||||
{
|
Vector2f pos(mChildren.at(i)->getPosition()[0], mChildren.at(i)->getPosition()[1]);
|
||||||
Vector2f pos(mChildren.at(i)->getPosition()[0], mChildren.at(i)->getPosition()[1]);
|
Vector2f bottomRight = mChildren.at(i)->getSize() + pos;
|
||||||
Vector2f bottomRight = mChildren.at(i)->getSize() + pos;
|
if (bottomRight.x() > max.x())
|
||||||
if(bottomRight.x() > max.x())
|
max.x() = bottomRight.x();
|
||||||
max.x() = bottomRight.x();
|
if (bottomRight.y() > max.y())
|
||||||
if(bottomRight.y() > max.y())
|
max.y() = bottomRight.y();
|
||||||
max.y() = bottomRight.y();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ScrollableContainer::reset()
|
void ScrollableContainer::reset()
|
||||||
{
|
{
|
||||||
mScrollPos = Vector2f(0, 0);
|
mScrollPos = Vector2f(0, 0);
|
||||||
mAutoScrollResetAccumulator = 0;
|
mAutoScrollResetAccumulator = 0;
|
||||||
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
mAutoScrollAccumulator = -mAutoScrollDelay + mAutoScrollSpeed;
|
||||||
mAtEnd = false;
|
mAtEnd = false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
//
|
||||||
|
// ScrollableContainer.h
|
||||||
|
//
|
||||||
|
// Area containing scrollable information, for example the game description
|
||||||
|
// text container in the detailed, video and grid views.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
#ifndef ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
||||||
#define ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
#define ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
||||||
|
@ -7,26 +14,26 @@
|
||||||
class ScrollableContainer : public GuiComponent
|
class ScrollableContainer : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ScrollableContainer(Window* window);
|
ScrollableContainer(Window* window);
|
||||||
|
|
||||||
Vector2f getScrollPos() const;
|
Vector2f getScrollPos() const;
|
||||||
void setScrollPos(const Vector2f& pos);
|
void setScrollPos(const Vector2f& pos);
|
||||||
void setAutoScroll(bool autoScroll);
|
void setAutoScroll(bool autoScroll);
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void update(int deltaTime) override;
|
void update(int deltaTime) override;
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Vector2f getContentSize();
|
Vector2f getContentSize();
|
||||||
|
|
||||||
Vector2f mScrollPos;
|
Vector2f mScrollPos;
|
||||||
Vector2f mScrollDir;
|
Vector2f mScrollDir;
|
||||||
int mAutoScrollDelay; // ms to wait before starting to autoscroll
|
int mAutoScrollDelay; // ms to wait before starting to autoscroll.
|
||||||
int mAutoScrollSpeed; // ms to wait before scrolling down by mScrollDir
|
int mAutoScrollSpeed; // ms to wait before scrolling down by mScrollDir.
|
||||||
int mAutoScrollAccumulator;
|
int mAutoScrollAccumulator;
|
||||||
bool mAtEnd;
|
bool mAtEnd;
|
||||||
int mAutoScrollResetAccumulator;
|
int mAutoScrollResetAccumulator;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
#endif // ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// SliderComponent.cpp
|
||||||
|
//
|
||||||
|
// Slider to set value in a predefined range.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/SliderComponent.h"
|
#include "components/SliderComponent.h"
|
||||||
|
|
||||||
#include "resources/Font.h"
|
#include "resources/Font.h"
|
||||||
|
@ -5,138 +11,149 @@
|
||||||
#define MOVE_REPEAT_DELAY 500
|
#define MOVE_REPEAT_DELAY 500
|
||||||
#define MOVE_REPEAT_RATE 40
|
#define MOVE_REPEAT_RATE 40
|
||||||
|
|
||||||
SliderComponent::SliderComponent(Window* window, float min, float max, float increment, const std::string& suffix) : GuiComponent(window),
|
SliderComponent::SliderComponent(
|
||||||
mMin(min), mMax(max), mSingleIncrement(increment), mMoveRate(0), mKnob(window), mSuffix(suffix)
|
Window* window,
|
||||||
|
float min,
|
||||||
|
float max,
|
||||||
|
float increment,
|
||||||
|
const std::string& suffix)
|
||||||
|
: GuiComponent(window),
|
||||||
|
mMin(min),
|
||||||
|
mMax(max),
|
||||||
|
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;
|
||||||
|
|
||||||
mKnob.setOrigin(0.5f, 0.5f);
|
mKnob.setOrigin(0.5f, 0.5f);
|
||||||
mKnob.setImage(":/graphics/slider_knob.svg");
|
mKnob.setImage(":/graphics/slider_knob.svg");
|
||||||
|
|
||||||
setSize(Renderer::getScreenWidth() * 0.15f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
|
setSize(Renderer::getScreenWidth() * 0.15f, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool SliderComponent::input(InputConfig* config, Input input)
|
bool SliderComponent::input(InputConfig* config, Input input)
|
||||||
{
|
{
|
||||||
if(config->isMappedLike("left", input))
|
if (config->isMappedLike("left", input)) {
|
||||||
{
|
if (input.value)
|
||||||
if(input.value)
|
setValue(mValue - mSingleIncrement);
|
||||||
setValue(mValue - mSingleIncrement);
|
|
||||||
|
|
||||||
mMoveRate = input.value ? -mSingleIncrement : 0;
|
mMoveRate = input.value ? -mSingleIncrement : 0;
|
||||||
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if(config->isMappedLike("right", input))
|
if (config->isMappedLike("right", input)) {
|
||||||
{
|
if (input.value)
|
||||||
if(input.value)
|
setValue(mValue + mSingleIncrement);
|
||||||
setValue(mValue + mSingleIncrement);
|
|
||||||
|
|
||||||
mMoveRate = input.value ? mSingleIncrement : 0;
|
mMoveRate = input.value ? mSingleIncrement : 0;
|
||||||
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
mMoveAccumulator = -MOVE_REPEAT_DELAY;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return GuiComponent::input(config, input);
|
return GuiComponent::input(config, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderComponent::update(int deltaTime)
|
void SliderComponent::update(int deltaTime)
|
||||||
{
|
{
|
||||||
if(mMoveRate != 0)
|
if (mMoveRate != 0) {
|
||||||
{
|
mMoveAccumulator += deltaTime;
|
||||||
mMoveAccumulator += deltaTime;
|
while (mMoveAccumulator >= MOVE_REPEAT_RATE) {
|
||||||
while(mMoveAccumulator >= MOVE_REPEAT_RATE)
|
setValue(mValue + mMoveRate);
|
||||||
{
|
mMoveAccumulator -= MOVE_REPEAT_RATE;
|
||||||
setValue(mValue + mMoveRate);
|
}
|
||||||
mMoveAccumulator -= MOVE_REPEAT_RATE;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
GuiComponent::update(deltaTime);
|
GuiComponent::update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderComponent::render(const Transform4x4f& parentTrans)
|
void SliderComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
// render suffix
|
// Render suffix.
|
||||||
if(mValueCache)
|
if (mValueCache)
|
||||||
mFont->renderTextCache(mValueCache.get());
|
mFont->renderTextCache(mValueCache.get());
|
||||||
|
|
||||||
float width = mSize.x() - mKnob.getSize().x() - (mValueCache ? mValueCache->metrics.size.x() + 4 : 0);
|
float width = mSize.x() - mKnob.getSize().x() -
|
||||||
|
(mValueCache ? mValueCache->metrics.size.x() + 4 : 0);
|
||||||
|
|
||||||
//render line
|
// Render line.
|
||||||
const float lineWidth = 2;
|
const float lineWidth = 2;
|
||||||
Renderer::drawRect(mKnob.getSize().x() / 2, mSize.y() / 2 - lineWidth / 2, width, lineWidth, 0x777777FF, 0x777777FF);
|
Renderer::drawRect(mKnob.getSize().x() / 2, mSize.y() / 2 -
|
||||||
|
lineWidth / 2, width, lineWidth, 0x777777FF, 0x777777FF);
|
||||||
|
|
||||||
//render knob
|
// Render knob.
|
||||||
mKnob.render(trans);
|
mKnob.render(trans);
|
||||||
|
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderComponent::setValue(float value)
|
void SliderComponent::setValue(float value)
|
||||||
{
|
{
|
||||||
mValue = value;
|
mValue = value;
|
||||||
if(mValue < mMin)
|
if (mValue < mMin)
|
||||||
mValue = mMin;
|
mValue = mMin;
|
||||||
else if(mValue > mMax)
|
else if (mValue > mMax)
|
||||||
mValue = mMax;
|
mValue = mMax;
|
||||||
|
|
||||||
onValueChanged();
|
onValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
float SliderComponent::getValue()
|
float SliderComponent::getValue()
|
||||||
{
|
{
|
||||||
return mValue;
|
return mValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderComponent::onSizeChanged()
|
void SliderComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
if(!mSuffix.empty())
|
if (!mSuffix.empty())
|
||||||
mFont = Font::get((int)(mSize.y()), FONT_PATH_LIGHT);
|
mFont = Font::get((int)(mSize.y()), FONT_PATH_LIGHT);
|
||||||
|
|
||||||
onValueChanged();
|
onValueChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SliderComponent::onValueChanged()
|
void SliderComponent::onValueChanged()
|
||||||
{
|
{
|
||||||
// update suffix textcache
|
// Update suffix textcache.
|
||||||
if(mFont)
|
if (mFont) {
|
||||||
{
|
std::stringstream ss;
|
||||||
std::stringstream ss;
|
ss << std::fixed;
|
||||||
ss << std::fixed;
|
ss.precision(0);
|
||||||
ss.precision(0);
|
ss << mValue;
|
||||||
ss << mValue;
|
ss << mSuffix;
|
||||||
ss << mSuffix;
|
const std::string val = ss.str();
|
||||||
const std::string val = ss.str();
|
|
||||||
|
|
||||||
ss.str("");
|
ss.str("");
|
||||||
ss.clear();
|
ss.clear();
|
||||||
ss << std::fixed;
|
ss << std::fixed;
|
||||||
ss.precision(0);
|
ss.precision(0);
|
||||||
ss << mMax;
|
ss << mMax;
|
||||||
ss << mSuffix;
|
ss << mSuffix;
|
||||||
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() - textSize.x(), (mSize.y() - textSize.y()) / 2, 0x777777FF));
|
mValueCache = std::shared_ptr<TextCache>(mFont->buildTextCache(val, mSize.x() -
|
||||||
mValueCache->metrics.size[0] = textSize.x(); // fudge the width
|
textSize.x(), (mSize.y() - textSize.y()) / 2, 0x777777FF));
|
||||||
}
|
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() - (mValueCache ? mValueCache->metrics.size.x() + 4 : 0);
|
float lineLength = mSize.x() - mKnob.getSize().x() -
|
||||||
mKnob.setPosition(((mValue + mMin) / mMax) * lineLength + mKnob.getSize().x()/2, mSize.y() / 2);
|
(mValueCache ? mValueCache->metrics.size.x() + 4 : 0);
|
||||||
|
mKnob.setPosition(((mValue + mMin) / mMax) * lineLength +
|
||||||
|
mKnob.getSize().x()/2, mSize.y() / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
|
std::vector<HelpPrompt> SliderComponent::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> prompts;
|
std::vector<HelpPrompt> prompts;
|
||||||
prompts.push_back(HelpPrompt("left/right", "change value"));
|
prompts.push_back(HelpPrompt("left/right", "change value"));
|
||||||
return prompts;
|
return prompts;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// SliderComponent.h
|
||||||
|
//
|
||||||
|
// Slider to set value in a predefined range.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -12,34 +18,40 @@ class TextCache;
|
||||||
class SliderComponent : public GuiComponent
|
class SliderComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
//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).
|
// Minimum value (far left of the slider), maximum value (far right of the slider),
|
||||||
SliderComponent(Window* window, float min, float max, float increment, const std::string& suffix = "");
|
// increment size (how much just pressing L/R moves by), unit to display (optional).
|
||||||
|
SliderComponent(
|
||||||
|
Window* window,
|
||||||
|
float min,
|
||||||
|
float max,
|
||||||
|
float increment,
|
||||||
|
const std::string& suffix = "");
|
||||||
|
|
||||||
void setValue(float val);
|
void setValue(float val);
|
||||||
float getValue();
|
float getValue();
|
||||||
|
|
||||||
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 onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void onValueChanged();
|
void onValueChanged();
|
||||||
|
|
||||||
float mMin, mMax;
|
float mMin, mMax;
|
||||||
float mValue;
|
float mValue;
|
||||||
float mSingleIncrement;
|
float mSingleIncrement;
|
||||||
float mMoveRate;
|
float mMoveRate;
|
||||||
int mMoveAccumulator;
|
int mMoveAccumulator;
|
||||||
|
|
||||||
ImageComponent mKnob;
|
ImageComponent mKnob;
|
||||||
|
|
||||||
std::string mSuffix;
|
std::string mSuffix;
|
||||||
std::shared_ptr<Font> mFont;
|
std::shared_ptr<Font> mFont;
|
||||||
std::shared_ptr<TextCache> mValueCache;
|
std::shared_ptr<TextCache> mValueCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
|
||||||
|
|
|
@ -1,295 +1,319 @@
|
||||||
|
//
|
||||||
|
// TextComponent.cpp
|
||||||
|
//
|
||||||
|
// Displays text.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/TextComponent.h"
|
#include "components/TextComponent.h"
|
||||||
|
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "Log.h"
|
#include "Log.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
TextComponent::TextComponent(Window* window) : GuiComponent(window),
|
TextComponent::TextComponent(
|
||||||
mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
|
Window* window)
|
||||||
mHorizontalAlignment(ALIGN_LEFT), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
|
: GuiComponent(window),
|
||||||
mRenderBackground(false)
|
mFont(Font::get(FONT_SIZE_MEDIUM)),
|
||||||
|
mUppercase(false),
|
||||||
|
mColor(0x000000FF),
|
||||||
|
mAutoCalcExtent(true, true),
|
||||||
|
mHorizontalAlignment(ALIGN_LEFT),
|
||||||
|
mVerticalAlignment(ALIGN_CENTER),
|
||||||
|
mLineSpacing(1.5f),
|
||||||
|
mBgColor(0),
|
||||||
|
mRenderBackground(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
TextComponent::TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Alignment align,
|
TextComponent::TextComponent(
|
||||||
Vector3f pos, Vector2f size, unsigned int bgcolor) : GuiComponent(window),
|
Window* window,
|
||||||
mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
|
const std::string& text,
|
||||||
mHorizontalAlignment(align), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
|
const std::shared_ptr<Font>& font,
|
||||||
mRenderBackground(false)
|
unsigned int color,
|
||||||
|
Alignment align,
|
||||||
|
Vector3f pos,
|
||||||
|
Vector2f size,
|
||||||
|
unsigned int bgcolor)
|
||||||
|
: GuiComponent(window),
|
||||||
|
mFont(nullptr),
|
||||||
|
mUppercase(false),
|
||||||
|
mColor(0x000000FF),
|
||||||
|
mAutoCalcExtent(true, true),
|
||||||
|
mHorizontalAlignment(align),
|
||||||
|
mVerticalAlignment(ALIGN_CENTER),
|
||||||
|
mLineSpacing(1.5f),
|
||||||
|
mBgColor(0),
|
||||||
|
mRenderBackground(false)
|
||||||
{
|
{
|
||||||
setFont(font);
|
setFont(font);
|
||||||
setColor(color);
|
setColor(color);
|
||||||
setBackgroundColor(bgcolor);
|
setBackgroundColor(bgcolor);
|
||||||
setText(text);
|
setText(text);
|
||||||
setPosition(pos);
|
setPosition(pos);
|
||||||
setSize(size);
|
setSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::onSizeChanged()
|
void TextComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0));
|
mAutoCalcExtent = Vector2i((getSize().x() == 0), (getSize().y() == 0));
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setFont(const std::shared_ptr<Font>& font)
|
void TextComponent::setFont(const std::shared_ptr<Font>& font)
|
||||||
{
|
{
|
||||||
mFont = font;
|
mFont = font;
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the color of the font/text
|
// Set the color of the font/text.
|
||||||
void TextComponent::setColor(unsigned int color)
|
void TextComponent::setColor(unsigned int color)
|
||||||
{
|
{
|
||||||
mColor = color;
|
mColor = color;
|
||||||
mColorOpacity = mColor & 0x000000FF;
|
mColorOpacity = mColor & 0x000000FF;
|
||||||
onColorChanged();
|
onColorChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the color of the background box
|
// Set the color of the background box.
|
||||||
void TextComponent::setBackgroundColor(unsigned int color)
|
void TextComponent::setBackgroundColor(unsigned int color)
|
||||||
{
|
{
|
||||||
mBgColor = color;
|
mBgColor = color;
|
||||||
mBgColorOpacity = mBgColor & 0x000000FF;
|
mBgColorOpacity = mBgColor & 0x000000FF;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setRenderBackground(bool render)
|
void TextComponent::setRenderBackground(bool render)
|
||||||
{
|
{
|
||||||
mRenderBackground = render;
|
mRenderBackground = render;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scale the opacity
|
// Scale the opacity.
|
||||||
void TextComponent::setOpacity(unsigned char opacity)
|
void TextComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
// This method is mostly called to do fading in-out of the Text component element.
|
// This method is mostly called to do fading in-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 int 0-255),
|
||||||
// of the opacity originally set with setColor() or setBackgroundColor().
|
// of the opacity originally set with setColor() or setBackgroundColor().
|
||||||
|
|
||||||
unsigned char o = (unsigned char)((float)opacity / 255.f * (float) mColorOpacity);
|
unsigned char o = (unsigned char)((float)opacity / 255.f * (float) mColorOpacity);
|
||||||
mColor = (mColor & 0xFFFFFF00) | (unsigned char) o;
|
mColor = (mColor & 0xFFFFFF00) | (unsigned char) o;
|
||||||
|
|
||||||
unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity);
|
unsigned char bgo = (unsigned char)((float)opacity / 255.f * (float)mBgColorOpacity);
|
||||||
mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo;
|
mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo;
|
||||||
|
|
||||||
onColorChanged();
|
onColorChanged();
|
||||||
|
GuiComponent::setOpacity(opacity);
|
||||||
GuiComponent::setOpacity(opacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char TextComponent::getOpacity() const
|
unsigned char TextComponent::getOpacity() const
|
||||||
{
|
{
|
||||||
return mColor & 0x000000FF;
|
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::setUppercase(bool uppercase)
|
void TextComponent::setUppercase(bool uppercase)
|
||||||
{
|
{
|
||||||
mUppercase = uppercase;
|
mUppercase = uppercase;
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::render(const Transform4x4f& parentTrans)
|
void TextComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
if (mRenderBackground)
|
if (mRenderBackground) {
|
||||||
{
|
Renderer::setMatrix(trans);
|
||||||
Renderer::setMatrix(trans);
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor);
|
||||||
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if(mTextCache)
|
if (mTextCache) {
|
||||||
{
|
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:
|
||||||
{
|
yOff = 0;
|
||||||
case ALIGN_TOP:
|
break;
|
||||||
yOff = 0;
|
case ALIGN_BOTTOM:
|
||||||
break;
|
yOff = (getSize().y() - textSize.y());
|
||||||
case ALIGN_BOTTOM:
|
break;
|
||||||
yOff = (getSize().y() - textSize.y());
|
case ALIGN_CENTER:
|
||||||
break;
|
yOff = (getSize().y() - textSize.y()) / 2.0f;
|
||||||
case ALIGN_CENTER:
|
break;
|
||||||
yOff = (getSize().y() - textSize.y()) / 2.0f;
|
default:
|
||||||
break;
|
break;
|
||||||
default:
|
}
|
||||||
break;
|
Vector3f off(0, yOff, 0);
|
||||||
}
|
|
||||||
Vector3f off(0, yOff, 0);
|
|
||||||
|
|
||||||
if(Settings::getInstance()->getBool("DebugText"))
|
if (Settings::getInstance()->getBool("DebugText")) {
|
||||||
{
|
// Draw the "textbox" area, what we are aligned within.
|
||||||
// draw the "textbox" area, what we are aligned within
|
Renderer::setMatrix(trans);
|
||||||
Renderer::setMatrix(trans);
|
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
|
||||||
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
trans.translate(off);
|
trans.translate(off);
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
// draw the text area, where the text actually is going
|
// 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:
|
||||||
{
|
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
|
||||||
case ALIGN_LEFT:
|
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
||||||
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
|
break;
|
||||||
break;
|
case ALIGN_CENTER:
|
||||||
case ALIGN_CENTER:
|
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f,
|
||||||
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 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(), 0x00000033, 0x00000033);
|
case ALIGN_RIGHT:
|
||||||
break;
|
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f,
|
||||||
default:
|
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
|
||||||
break;
|
0x00000033, 0x00000033);
|
||||||
}
|
break;
|
||||||
}
|
default:
|
||||||
mFont->renderTextCache(mTextCache.get());
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
mFont->renderTextCache(mTextCache.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::calculateExtent()
|
void TextComponent::calculateExtent()
|
||||||
{
|
{
|
||||||
if(mAutoCalcExtent.x())
|
if (mAutoCalcExtent.x()) {
|
||||||
{
|
mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing);
|
||||||
mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing);
|
}
|
||||||
}else{
|
else {
|
||||||
if(mAutoCalcExtent.y())
|
if (mAutoCalcExtent.y())
|
||||||
{
|
mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText)
|
||||||
mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, getSize().x(), mLineSpacing).y();
|
: mText, getSize().x(), mLineSpacing).y();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::onTextChanged()
|
void TextComponent::onTextChanged()
|
||||||
{
|
{
|
||||||
calculateExtent();
|
calculateExtent();
|
||||||
|
|
||||||
if(!mFont || mText.empty())
|
if (!mFont || mText.empty()) {
|
||||||
{
|
mTextCache.reset();
|
||||||
mTextCache.reset();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
std::string text = mUppercase ? Utils::String::toUpper(mText) : mText;
|
std::string text = mUppercase ? Utils::String::toUpper(mText) : mText;
|
||||||
|
|
||||||
std::shared_ptr<Font> f = mFont;
|
std::shared_ptr<Font> f = mFont;
|
||||||
const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight()*1.2f);
|
const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight()*1.2f);
|
||||||
|
|
||||||
bool addAbbrev = false;
|
bool addAbbrev = false;
|
||||||
if(!isMultiline)
|
if (!isMultiline) {
|
||||||
{
|
size_t newline = text.find('\n');
|
||||||
size_t newline = text.find('\n');
|
// Single line of text - stop at the first newline since it'll mess everything up.
|
||||||
text = text.substr(0, newline); // single line of text - stop at the first newline since it'll mess everything up
|
text = text.substr(0, newline);
|
||||||
addAbbrev = newline != std::string::npos;
|
addAbbrev = newline != std::string::npos;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2f size = f->sizeText(text);
|
Vector2f size = f->sizeText(text);
|
||||||
if(!isMultiline && mSize.x() && text.size() && (size.x() > mSize.x() || addAbbrev))
|
if (!isMultiline && mSize.x() && text.size() && (size.x() > mSize.x() || addAbbrev)) {
|
||||||
{
|
// Abbreviate text.
|
||||||
// abbreviate text
|
const std::string abbrev = "...";
|
||||||
const std::string abbrev = "...";
|
Vector2f abbrevSize = f->sizeText(abbrev);
|
||||||
Vector2f abbrevSize = f->sizeText(abbrev);
|
|
||||||
|
|
||||||
while(text.size() && size.x() + abbrevSize.x() > mSize.x())
|
while (text.size() && size.x() + abbrevSize.x() > mSize.x()) {
|
||||||
{
|
size_t newSize = Utils::String::prevCursor(text, text.size());
|
||||||
size_t newSize = Utils::String::prevCursor(text, text.size());
|
text.erase(newSize, text.size() - newSize);
|
||||||
text.erase(newSize, text.size() - newSize);
|
size = f->sizeText(text);
|
||||||
size = f->sizeText(text);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
text.append(abbrev);
|
text.append(abbrev);
|
||||||
|
|
||||||
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing));
|
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, Vector2f(0, 0),
|
||||||
}else{
|
(mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing));
|
||||||
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(f->wrapText(text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(), mHorizontalAlignment, mLineSpacing));
|
}
|
||||||
}
|
else {
|
||||||
|
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(f->wrapText(
|
||||||
|
text, mSize.x()), Vector2f(0, 0), (mColor >> 8 << 8) | mOpacity, mSize.x(),
|
||||||
|
mHorizontalAlignment, mLineSpacing));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::onColorChanged()
|
void TextComponent::onColorChanged()
|
||||||
{
|
{
|
||||||
if(mTextCache)
|
if (mTextCache)
|
||||||
{
|
mTextCache->setColor(mColor);
|
||||||
mTextCache->setColor(mColor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setHorizontalAlignment(Alignment align)
|
void TextComponent::setHorizontalAlignment(Alignment align)
|
||||||
{
|
{
|
||||||
mHorizontalAlignment = align;
|
mHorizontalAlignment = 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)
|
||||||
{
|
{
|
||||||
mLineSpacing = spacing;
|
mLineSpacing = spacing;
|
||||||
onTextChanged();
|
onTextChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::setValue(const std::string& value)
|
void TextComponent::setValue(const std::string& value)
|
||||||
{
|
{
|
||||||
setText(value);
|
setText(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TextComponent::getValue() const
|
std::string TextComponent::getValue() const
|
||||||
{
|
{
|
||||||
return mText;
|
return mText;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
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);
|
||||||
|
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "text");
|
||||||
if(!elem)
|
if (!elem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (properties & COLOR && elem->has("color"))
|
if (properties & COLOR && elem->has("color"))
|
||||||
setColor(elem->get<unsigned int>("color"));
|
setColor(elem->get<unsigned int>("color"));
|
||||||
|
|
||||||
setRenderBackground(false);
|
setRenderBackground(false);
|
||||||
if (properties & COLOR && elem->has("backgroundColor")) {
|
if (properties & COLOR && elem->has("backgroundColor")) {
|
||||||
setBackgroundColor(elem->get<unsigned int>("backgroundColor"));
|
setBackgroundColor(elem->get<unsigned int>("backgroundColor"));
|
||||||
setRenderBackground(true);
|
setRenderBackground(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(properties & ALIGNMENT && elem->has("alignment"))
|
if (properties & ALIGNMENT && elem->has("alignment")) {
|
||||||
{
|
std::string str = elem->get<std::string>("alignment");
|
||||||
std::string str = elem->get<std::string>("alignment");
|
if (str == "left")
|
||||||
if(str == "left")
|
setHorizontalAlignment(ALIGN_LEFT);
|
||||||
setHorizontalAlignment(ALIGN_LEFT);
|
else if (str == "center")
|
||||||
else if(str == "center")
|
setHorizontalAlignment(ALIGN_CENTER);
|
||||||
setHorizontalAlignment(ALIGN_CENTER);
|
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 & TEXT && elem->has("text"))
|
if (properties & TEXT && elem->has("text"))
|
||||||
setText(elem->get<std::string>("text"));
|
setText(elem->get<std::string>("text"));
|
||||||
|
|
||||||
if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
||||||
setUppercase(elem->get<bool>("forceUppercase"));
|
setUppercase(elem->get<bool>("forceUppercase"));
|
||||||
|
|
||||||
if(properties & LINE_SPACING && elem->has("lineSpacing"))
|
if (properties & LINE_SPACING && elem->has("lineSpacing"))
|
||||||
setLineSpacing(elem->get<float>("lineSpacing"));
|
setLineSpacing(elem->get<float>("lineSpacing"));
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
setFont(Font::getFromTheme(elem, properties, mFont));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// TextComponent.h
|
||||||
|
//
|
||||||
|
// Displays text.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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
|
||||||
|
@ -9,63 +15,73 @@ class ThemeData;
|
||||||
|
|
||||||
// Used to display text.
|
// Used to display text.
|
||||||
// TextComponent::setSize(x, y) works a little differently than most components:
|
// TextComponent::setSize(x, y) works a little differently than most components:
|
||||||
// * (0, 0) - will automatically calculate a size that fits the text on one line (expand horizontally)
|
// * (0, 0) - Will automatically calculate a size that fits
|
||||||
// * (x != 0, 0) - wrap text so that it does not reach beyond x. Will automatically calculate a vertical size (expand vertically).
|
// the text on one line (expand horizontally).
|
||||||
// * (x != 0, y <= fontHeight) - will truncate text so it fits within this box.
|
// * (x != 0, 0) - Wrap text so that it does not reach beyond x. Will
|
||||||
|
// automatically calculate a vertical size (expand vertically).
|
||||||
|
// * (x != 0, y <= fontHeight) - Will truncate text so it fits within this box.
|
||||||
class TextComponent : public GuiComponent
|
class TextComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextComponent(Window* window);
|
TextComponent(Window* window);
|
||||||
TextComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color = 0x000000FF, Alignment align = ALIGN_LEFT,
|
TextComponent(
|
||||||
Vector3f pos = Vector3f::Zero(), Vector2f size = Vector2f::Zero(), unsigned int bgcolor = 0x00000000);
|
Window* window,
|
||||||
|
const std::string& text,
|
||||||
|
const std::shared_ptr<Font>& font,
|
||||||
|
unsigned int color = 0x000000FF,
|
||||||
|
Alignment align = ALIGN_LEFT,
|
||||||
|
Vector3f pos = Vector3f::Zero(),
|
||||||
|
Vector2f size = Vector2f::Zero(),
|
||||||
|
unsigned int bgcolor = 0x00000000);
|
||||||
|
|
||||||
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 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 setBackgroundColor(unsigned int color);
|
void setBackgroundColor(unsigned int color);
|
||||||
void setRenderBackground(bool render);
|
void setRenderBackground(bool render);
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
std::string getValue() const override;
|
std::string getValue() const override;
|
||||||
void setValue(const std::string& value) override;
|
void setValue(const std::string& value) override;
|
||||||
|
|
||||||
unsigned char getOpacity() const override;
|
unsigned char getOpacity() const override;
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
|
|
||||||
inline std::shared_ptr<Font> getFont() const { return mFont; }
|
inline std::shared_ptr<Font> getFont() const { return mFont; }
|
||||||
|
|
||||||
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
||||||
|
const std::string& element, unsigned int properties) override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onTextChanged();
|
virtual void onTextChanged();
|
||||||
|
|
||||||
std::string mText;
|
std::string mText;
|
||||||
std::shared_ptr<Font> mFont;
|
std::shared_ptr<Font> mFont;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void calculateExtent();
|
void calculateExtent();
|
||||||
|
|
||||||
void onColorChanged();
|
void onColorChanged();
|
||||||
|
|
||||||
unsigned int mColor;
|
unsigned int mColor;
|
||||||
unsigned int mBgColor;
|
unsigned int mBgColor;
|
||||||
unsigned char mColorOpacity;
|
unsigned char mColorOpacity;
|
||||||
unsigned char mBgColorOpacity;
|
unsigned char mBgColorOpacity;
|
||||||
bool mRenderBackground;
|
bool mRenderBackground;
|
||||||
|
|
||||||
bool mUppercase;
|
bool mUppercase;
|
||||||
Vector2i mAutoCalcExtent;
|
Vector2i mAutoCalcExtent;
|
||||||
std::shared_ptr<TextCache> mTextCache;
|
std::shared_ptr<TextCache> mTextCache;
|
||||||
Alignment mHorizontalAlignment;
|
Alignment mHorizontalAlignment;
|
||||||
Alignment mVerticalAlignment;
|
Alignment mVerticalAlignment;
|
||||||
float mLineSpacing;
|
float mLineSpacing;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_TEXT_COMPONENT_H
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// TextListComponent.h
|
// TextListComponent.h
|
||||||
//
|
//
|
||||||
// Used for displaying and navigating the gamelists.
|
// Used for displaying and navigating the gamelists.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
@ -18,8 +18,8 @@
|
||||||
class TextCache;
|
class TextCache;
|
||||||
|
|
||||||
struct TextListData {
|
struct TextListData {
|
||||||
unsigned int colorId;
|
unsigned int colorId;
|
||||||
std::shared_ptr<TextCache> textCache;
|
std::shared_ptr<TextCache> textCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
// A graphical list. Supports multiple colors for rows and scrolling.
|
// A graphical list. Supports multiple colors for rows and scrolling.
|
||||||
|
@ -27,441 +27,441 @@ 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;
|
||||||
using IList<TextListData, T>::listUpdate;
|
using IList<TextListData, T>::listUpdate;
|
||||||
using IList<TextListData, T>::listInput;
|
using IList<TextListData, T>::listInput;
|
||||||
using IList<TextListData, T>::listRenderTitleOverlay;
|
using IList<TextListData, T>::listRenderTitleOverlay;
|
||||||
using IList<TextListData, T>::getTransform;
|
using IList<TextListData, T>::getTransform;
|
||||||
using IList<TextListData, T>::mSize;
|
using IList<TextListData, T>::mSize;
|
||||||
using IList<TextListData, T>::mCursor;
|
using IList<TextListData, T>::mCursor;
|
||||||
// The following change is required for compilation with Clang.
|
// The following change is required for compilation with Clang.
|
||||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070
|
// http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2070
|
||||||
// using IList<TextListData, T>::Entry;
|
// using IList<TextListData, T>::Entry;
|
||||||
using IList<TextListData, T>::IList;
|
using IList<TextListData, T>::IList;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using IList<TextListData, T>::size;
|
using IList<TextListData, T>::size;
|
||||||
using IList<TextListData, T>::isScrolling;
|
using IList<TextListData, T>::isScrolling;
|
||||||
using IList<TextListData, T>::stopScrolling;
|
using IList<TextListData, T>::stopScrolling;
|
||||||
|
|
||||||
TextListComponent(Window* window);
|
TextListComponent(Window* window);
|
||||||
|
|
||||||
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& view,
|
||||||
const std::string& element, unsigned int properties) override;
|
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,
|
||||||
ALIGN_CENTER,
|
ALIGN_CENTER,
|
||||||
ALIGN_RIGHT
|
ALIGN_RIGHT
|
||||||
};
|
};
|
||||||
|
|
||||||
inline void setAlignment(Alignment align) { mAlignment = align; }
|
inline void setAlignment(Alignment align) { mAlignment = align; }
|
||||||
|
|
||||||
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
|
||||||
{ mCursorChangedCallback = func; }
|
{ mCursorChangedCallback = func; }
|
||||||
|
|
||||||
inline void setFont(const std::shared_ptr<Font>& font)
|
inline 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*/)
|
inline 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; }
|
inline void setSelectorHeight(float selectorScale) { mSelectorHeight = selectorScale; }
|
||||||
inline void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; }
|
inline void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; }
|
||||||
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
|
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
|
||||||
inline void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; }
|
inline void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; }
|
||||||
inline void setSelectorColorGradientHorizontal(bool horizontal)
|
inline void setSelectorColorGradientHorizontal(bool horizontal)
|
||||||
{ mSelectorColorGradientHorizontal = horizontal; }
|
{ mSelectorColorGradientHorizontal = horizontal; }
|
||||||
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
|
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
|
||||||
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
|
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
|
||||||
inline void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; }
|
inline void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
virtual void onScroll() override {
|
virtual void onScroll() override {
|
||||||
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); }
|
NavigationSounds::getInstance()->playThemeNavigationSound(SCROLLSOUND); }
|
||||||
virtual void onCursorChanged(const CursorState& state) override;
|
virtual void onCursorChanged(const CursorState& state) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int mMarqueeOffset;
|
int mMarqueeOffset;
|
||||||
int mMarqueeOffset2;
|
int mMarqueeOffset2;
|
||||||
int mMarqueeTime;
|
int mMarqueeTime;
|
||||||
|
|
||||||
Alignment mAlignment;
|
Alignment mAlignment;
|
||||||
float mHorizontalMargin;
|
float mHorizontalMargin;
|
||||||
|
|
||||||
std::function<void(CursorState state)> mCursorChangedCallback;
|
std::function<void(CursorState state)> mCursorChangedCallback;
|
||||||
|
|
||||||
std::shared_ptr<Font> mFont;
|
std::shared_ptr<Font> mFont;
|
||||||
bool mUppercase;
|
bool mUppercase;
|
||||||
float mLineSpacing;
|
float mLineSpacing;
|
||||||
float mSelectorHeight;
|
float mSelectorHeight;
|
||||||
float mSelectorOffsetY;
|
float mSelectorOffsetY;
|
||||||
unsigned int mSelectorColor;
|
unsigned int mSelectorColor;
|
||||||
unsigned int mSelectorColorEnd;
|
unsigned int mSelectorColorEnd;
|
||||||
bool mSelectorColorGradientHorizontal = true;
|
bool mSelectorColorGradientHorizontal = true;
|
||||||
unsigned int mSelectedColor;
|
unsigned int mSelectedColor;
|
||||||
static const unsigned int COLOR_ID_COUNT = 2;
|
static const unsigned int COLOR_ID_COUNT = 2;
|
||||||
unsigned int mColors[COLOR_ID_COUNT];
|
unsigned int mColors[COLOR_ID_COUNT];
|
||||||
|
|
||||||
ImageComponent mSelectorImage;
|
ImageComponent mSelectorImage;
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
||||||
mMarqueeTime = 0;
|
mMarqueeTime = 0;
|
||||||
|
|
||||||
mHorizontalMargin = 0;
|
mHorizontalMargin = 0;
|
||||||
mAlignment = ALIGN_CENTER;
|
mAlignment = ALIGN_CENTER;
|
||||||
|
|
||||||
mFont = Font::get(FONT_SIZE_MEDIUM);
|
mFont = Font::get(FONT_SIZE_MEDIUM);
|
||||||
mUppercase = false;
|
mUppercase = false;
|
||||||
mLineSpacing = 1.5f;
|
mLineSpacing = 1.5f;
|
||||||
mSelectorHeight = mFont->getSize() * 1.5f;
|
mSelectorHeight = mFont->getSize() * 1.5f;
|
||||||
mSelectorOffsetY = 0;
|
mSelectorOffsetY = 0;
|
||||||
mSelectorColor = 0x000000FF;
|
mSelectorColor = 0x000000FF;
|
||||||
mSelectorColorEnd = 0x000000FF;
|
mSelectorColorEnd = 0x000000FF;
|
||||||
mSelectorColorGradientHorizontal = true;
|
mSelectorColorGradientHorizontal = true;
|
||||||
mSelectedColor = 0;
|
mSelectedColor = 0;
|
||||||
mColors[0] = 0x0000FFFF;
|
mColors[0] = 0x0000FFFF;
|
||||||
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)
|
||||||
{
|
{
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
|
|
||||||
std::shared_ptr<Font>& font = mFont;
|
std::shared_ptr<Font>& font = mFont;
|
||||||
|
|
||||||
if (size() == 0)
|
if (size() == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const float entrySize = Math::max(font->getHeight(1.0), (float)font->getSize()) * mLineSpacing;
|
const float entrySize = Math::max(font->getHeight(1.0), (float)font->getSize()) * mLineSpacing;
|
||||||
|
|
||||||
int startEntry = 0;
|
int startEntry = 0;
|
||||||
|
|
||||||
// Number of entries that can fit on the screen simultaniously.
|
// Number of entries that can fit on the screen simultaniously.
|
||||||
int screenCount = (int)(mSize.y() / entrySize + 0.5f);
|
int screenCount = (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)
|
||||||
startEntry = size() - screenCount;
|
startEntry = size() - screenCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
float y = 0;
|
float y = 0;
|
||||||
|
|
||||||
int listCutoff = startEntry + screenCount;
|
int listCutoff = startEntry + screenCount;
|
||||||
if (listCutoff > size())
|
if (listCutoff > size())
|
||||||
listCutoff = size();
|
listCutoff = size();
|
||||||
|
|
||||||
// Draw selector bar.
|
// Draw selector bar.
|
||||||
if (startEntry < listCutoff) {
|
if (startEntry < listCutoff) {
|
||||||
if (mSelectorImage.hasImage()) {
|
if (mSelectorImage.hasImage()) {
|
||||||
mSelectorImage.setPosition(0.f,
|
mSelectorImage.setPosition(0.f,
|
||||||
(mCursor - startEntry)*entrySize + mSelectorOffsetY, 0.f);
|
(mCursor - startEntry)*entrySize + mSelectorOffsetY, 0.f);
|
||||||
mSelectorImage.render(trans);
|
mSelectorImage.render(trans);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
Renderer::drawRect(
|
Renderer::drawRect(
|
||||||
0.0f,
|
0.0f,
|
||||||
(mCursor - startEntry)*entrySize +
|
(mCursor - startEntry)*entrySize +
|
||||||
mSelectorOffsetY,
|
mSelectorOffsetY,
|
||||||
mSize.x(),
|
mSize.x(),
|
||||||
mSelectorHeight,
|
mSelectorHeight,
|
||||||
mSelectorColor,
|
mSelectorColor,
|
||||||
mSelectorColorEnd,
|
mSelectorColorEnd,
|
||||||
mSelectorColorGradientHorizontal);
|
mSelectorColorGradientHorizontal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clip to inside margins.
|
// Clip to inside margins.
|
||||||
Vector3f dim(mSize.x(), mSize.y(), 0);
|
Vector3f dim(mSize.x(), mSize.y(), 0);
|
||||||
dim = trans * dim - trans.translation();
|
dim = trans * dim - trans.translation();
|
||||||
Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin),
|
Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin),
|
||||||
(int)trans.translation().y()), Vector2i((int)(dim.x() - mHorizontalMargin*2),
|
(int)trans.translation().y()), Vector2i((int)(dim.x() - mHorizontalMargin*2),
|
||||||
(int)dim.y()));
|
(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((unsigned int)i);
|
typename IList<TextListData, T>::Entry& entry = mEntries.at((unsigned int)i);
|
||||||
|
|
||||||
unsigned int color;
|
unsigned int color;
|
||||||
if (mCursor == i && mSelectedColor)
|
if (mCursor == i && mSelectedColor)
|
||||||
color = mSelectedColor;
|
color = mSelectedColor;
|
||||||
else
|
else
|
||||||
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(mUppercase ?
|
(font->buildTextCache(mUppercase ?
|
||||||
Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
|
Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
|
||||||
|
|
||||||
entry.data.textCache->setColor(color);
|
entry.data.textCache->setColor(color);
|
||||||
|
|
||||||
Vector3f offset(0, y, 0);
|
Vector3f offset(0, y, 0);
|
||||||
|
|
||||||
switch (mAlignment) {
|
switch (mAlignment) {
|
||||||
case ALIGN_LEFT:
|
case ALIGN_LEFT:
|
||||||
offset[0] = mHorizontalMargin;
|
offset[0] = mHorizontalMargin;
|
||||||
break;
|
break;
|
||||||
case ALIGN_CENTER:
|
case ALIGN_CENTER:
|
||||||
offset[0] = (int)((mSize.x() - entry.data.textCache->metrics.size.x()) / 2);
|
offset[0] = (int)((mSize.x() - entry.data.textCache->metrics.size.x()) / 2);
|
||||||
if (offset[0] < mHorizontalMargin)
|
if (offset[0] < mHorizontalMargin)
|
||||||
offset[0] = mHorizontalMargin;
|
offset[0] = mHorizontalMargin;
|
||||||
break;
|
break;
|
||||||
case ALIGN_RIGHT:
|
case ALIGN_RIGHT:
|
||||||
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
|
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
|
||||||
offset[0] -= mHorizontalMargin;
|
offset[0] -= mHorizontalMargin;
|
||||||
if (offset[0] < mHorizontalMargin)
|
if (offset[0] < mHorizontalMargin)
|
||||||
offset[0] = mHorizontalMargin;
|
offset[0] = mHorizontalMargin;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Render text.
|
// Render text.
|
||||||
Transform4x4f drawTrans = trans;
|
Transform4x4f drawTrans = trans;
|
||||||
|
|
||||||
// Currently selected item text might be scrolling.
|
// Currently selected item text might be scrolling.
|
||||||
if ((mCursor == i) && (mMarqueeOffset > 0))
|
if ((mCursor == i) && (mMarqueeOffset > 0))
|
||||||
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset, 0, 0));
|
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset, 0, 0));
|
||||||
else
|
else
|
||||||
drawTrans.translate(offset);
|
drawTrans.translate(offset);
|
||||||
|
|
||||||
Renderer::setMatrix(drawTrans);
|
Renderer::setMatrix(drawTrans);
|
||||||
font->renderTextCache(entry.data.textCache.get());
|
font->renderTextCache(entry.data.textCache.get());
|
||||||
|
|
||||||
// Render currently selected item text again if marquee is
|
// Render currently selected item text again if marquee is
|
||||||
// scrolled far enough for it to repeat.
|
// scrolled far enough for it to repeat.
|
||||||
if ((mCursor == i) && (mMarqueeOffset2 < 0)) {
|
if ((mCursor == i) && (mMarqueeOffset2 < 0)) {
|
||||||
drawTrans = trans;
|
drawTrans = trans;
|
||||||
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset2, 0, 0));
|
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset2, 0, 0));
|
||||||
Renderer::setMatrix(drawTrans);
|
Renderer::setMatrix(drawTrans);
|
||||||
font->renderTextCache(entry.data.textCache.get());
|
font->renderTextCache(entry.data.textCache.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
y += entrySize;
|
y += entrySize;
|
||||||
}
|
}
|
||||||
|
|
||||||
Renderer::popClipRect();
|
Renderer::popClipRect();
|
||||||
listRenderTitleOverlay(trans);
|
listRenderTitleOverlay(trans);
|
||||||
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) {
|
||||||
if (config->isMappedLike("down", input)) {
|
if (config->isMappedLike("down", input)) {
|
||||||
listInput(1);
|
listInput(1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->isMappedLike("up", input)) {
|
if (config->isMappedLike("up", input)) {
|
||||||
listInput(-1);
|
listInput(-1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (config->isMappedLike("rightshoulder", input)) {
|
if (config->isMappedLike("rightshoulder", input)) {
|
||||||
listInput(10);
|
listInput(10);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->isMappedLike("leftshoulder", input)) {
|
if (config->isMappedLike("leftshoulder", input)) {
|
||||||
listInput(-10);
|
listInput(-10);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->isMappedLike("righttrigger", input)) {
|
if (config->isMappedLike("righttrigger", input)) {
|
||||||
return this->listLastRow();
|
return this->listLastRow();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config->isMappedLike("lefttrigger", input)) {
|
if (config->isMappedLike("lefttrigger", input)) {
|
||||||
return this->listFirstRow();
|
return this->listFirstRow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Explicitly stop the scrolling, otherwise it will go forever in case
|
// Explicitly stop the scrolling, otherwise it will go forever in case
|
||||||
// the menu was openened or another gamelist was selected using the
|
// the menu was openened or another gamelist was selected using the
|
||||||
// quick system selector etc.
|
// quick system selector etc.
|
||||||
stopScrolling();
|
stopScrolling();
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
if (!isScrolling() && size() > 0) {
|
if (!isScrolling() && size() > 0) {
|
||||||
// Always reset the marquee offsets.
|
// Always reset the marquee offsets.
|
||||||
mMarqueeOffset = 0;
|
mMarqueeOffset = 0;
|
||||||
mMarqueeOffset2 = 0;
|
mMarqueeOffset2 = 0;
|
||||||
|
|
||||||
// If we're not scrolling and this object's text goes outside our size, marquee it!
|
// If we're not scrolling and this object's text goes outside our size, marquee it!
|
||||||
const float textLength = mFont->sizeText(mEntries.at((unsigned int)mCursor).name).x();
|
const float textLength = mFont->sizeText(mEntries.at((unsigned int)mCursor).name).x();
|
||||||
const float limit = mSize.x() - mHorizontalMargin * 2;
|
const float limit = mSize.x() - mHorizontalMargin * 2;
|
||||||
|
|
||||||
if (textLength > limit) {
|
if (textLength > limit) {
|
||||||
// Loop.
|
// Loop.
|
||||||
// Pixels per second (based on nes-mini font at 1920x1080 to produce a speed of 200).
|
// Pixels per second (based on nes-mini font at 1920x1080 to produce a speed of 200).
|
||||||
const float speed = mFont->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x() * 0.247f;
|
const float speed = mFont->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x() * 0.247f;
|
||||||
const float delay = 3000;
|
const float delay = 3000;
|
||||||
const float scrollLength = textLength;
|
const float scrollLength = textLength;
|
||||||
const float returnLength = speed * 1.5f;
|
const float returnLength = speed * 1.5f;
|
||||||
const float scrollTime = (scrollLength * 1000) / speed;
|
const float scrollTime = (scrollLength * 1000) / speed;
|
||||||
const float returnTime = (returnLength * 1000) / speed;
|
const float returnTime = (returnLength * 1000) / speed;
|
||||||
const int maxTime = (int)(delay + scrollTime + returnTime);
|
const int maxTime = (int)(delay + scrollTime + returnTime);
|
||||||
|
|
||||||
mMarqueeTime += deltaTime;
|
mMarqueeTime += deltaTime;
|
||||||
while (mMarqueeTime > maxTime)
|
while (mMarqueeTime > maxTime)
|
||||||
mMarqueeTime -= maxTime;
|
mMarqueeTime -= maxTime;
|
||||||
|
|
||||||
mMarqueeOffset = (int)(Math::Scroll::loop(delay, scrollTime + returnTime,
|
mMarqueeOffset = (int)(Math::Scroll::loop(delay, scrollTime + returnTime,
|
||||||
(float)mMarqueeTime, scrollLength + returnLength));
|
(float)mMarqueeTime, scrollLength + returnLength));
|
||||||
|
|
||||||
if (mMarqueeOffset > (scrollLength - (limit - returnLength)))
|
if (mMarqueeOffset > (scrollLength - (limit - returnLength)))
|
||||||
mMarqueeOffset2 = (int)(mMarqueeOffset - (scrollLength + returnLength));
|
mMarqueeOffset2 = (int)(mMarqueeOffset - (scrollLength + returnLength));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GuiComponent::update(deltaTime);
|
GuiComponent::update(deltaTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// List management stuff.
|
// List management stuff.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
|
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
|
||||||
{
|
{
|
||||||
assert(color < COLOR_ID_COUNT);
|
assert(color < COLOR_ID_COUNT);
|
||||||
|
|
||||||
typename IList<TextListData, T>::Entry entry;
|
typename IList<TextListData, T>::Entry entry;
|
||||||
entry.name = name;
|
entry.name = name;
|
||||||
entry.object = obj;
|
entry.object = obj;
|
||||||
entry.data.colorId = color;
|
entry.data.colorId = color;
|
||||||
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;
|
||||||
mMarqueeTime = 0;
|
mMarqueeTime = 0;
|
||||||
|
|
||||||
if (mCursorChangedCallback)
|
if (mCursorChangedCallback)
|
||||||
mCursorChangedCallback(state);
|
mCursorChangedCallback(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);
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "textlist");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "textlist");
|
||||||
if (!elem)
|
if (!elem)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
using namespace ThemeFlags;
|
using namespace ThemeFlags;
|
||||||
if (properties & COLOR) {
|
if (properties & COLOR) {
|
||||||
if (elem->has("selectorColor")) {
|
if (elem->has("selectorColor")) {
|
||||||
setSelectorColor(elem->get<unsigned int>("selectorColor"));
|
setSelectorColor(elem->get<unsigned int>("selectorColor"));
|
||||||
setSelectorColorEnd(elem->get<unsigned int>("selectorColor"));
|
setSelectorColorEnd(elem->get<unsigned int>("selectorColor"));
|
||||||
}
|
}
|
||||||
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(!(elem->get<std::string>
|
||||||
("selectorGradientType").compare("horizontal")));
|
("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"))
|
||||||
setColor(0, elem->get<unsigned int>("primaryColor"));
|
setColor(0, elem->get<unsigned int>("primaryColor"));
|
||||||
if (elem->has("secondaryColor"))
|
if (elem->has("secondaryColor"))
|
||||||
setColor(1, elem->get<unsigned int>("secondaryColor"));
|
setColor(1, elem->get<unsigned int>("secondaryColor"));
|
||||||
}
|
}
|
||||||
|
|
||||||
setFont(Font::getFromTheme(elem, properties, mFont));
|
setFont(Font::getFromTheme(elem, properties, mFont));
|
||||||
const float selectorHeight = Math::max(mFont->getHeight(1.0),
|
const float selectorHeight = Math::max(mFont->getHeight(1.0),
|
||||||
(float)mFont->getSize()) * mLineSpacing;
|
(float)mFont->getSize()) * mLineSpacing;
|
||||||
setSelectorHeight(selectorHeight);
|
setSelectorHeight(selectorHeight);
|
||||||
|
|
||||||
if (properties & ALIGNMENT) {
|
if (properties & ALIGNMENT) {
|
||||||
if (elem->has("alignment")) {
|
if (elem->has("alignment")) {
|
||||||
const std::string& str = elem->get<std::string>("alignment");
|
const std::string& str = elem->get<std::string>("alignment");
|
||||||
if (str == "left")
|
if (str == "left")
|
||||||
setAlignment(ALIGN_LEFT);
|
setAlignment(ALIGN_LEFT);
|
||||||
else if (str == "center")
|
else if (str == "center")
|
||||||
setAlignment(ALIGN_CENTER);
|
setAlignment(ALIGN_CENTER);
|
||||||
else if (str == "right")
|
else if (str == "right")
|
||||||
setAlignment(ALIGN_RIGHT);
|
setAlignment(ALIGN_RIGHT);
|
||||||
else
|
else
|
||||||
LOG(LogError) << "Unknown TextListComponent alignment \"" << str << "\"!";
|
LOG(LogError) << "Unknown TextListComponent alignment \"" << str << "\"!";
|
||||||
}
|
}
|
||||||
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() :
|
||||||
(float)Renderer::getScreenWidth());
|
(float)Renderer::getScreenWidth());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
|
||||||
setUppercase(elem->get<bool>("forceUppercase"));
|
setUppercase(elem->get<bool>("forceUppercase"));
|
||||||
|
|
||||||
if (properties & LINE_SPACING) {
|
if (properties & LINE_SPACING) {
|
||||||
if (elem->has("lineSpacing"))
|
if (elem->has("lineSpacing"))
|
||||||
setLineSpacing(elem->get<float>("lineSpacing"));
|
setLineSpacing(elem->get<float>("lineSpacing"));
|
||||||
if (elem->has("selectorHeight"))
|
if (elem->has("selectorHeight"))
|
||||||
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)Renderer::getScreenHeight();
|
float scale = this->mParent ? this->mParent->getSize().y() : (float)Renderer::getScreenHeight();
|
||||||
setSelectorOffsetY(elem->get<float>("selectorOffsetY") * scale);
|
setSelectorOffsetY(elem->get<float>("selectorOffsetY") * scale);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setSelectorOffsetY(0.0);
|
setSelectorOffsetY(0.0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (elem->has("selectorImagePath")) {
|
if (elem->has("selectorImagePath")) {
|
||||||
std::string path = elem->get<std::string>("selectorImagePath");
|
std::string path = elem->get<std::string>("selectorImagePath");
|
||||||
bool tile = elem->has("selectorImageTile") && elem->get<bool>("selectorImageTile");
|
bool tile = elem->has("selectorImageTile") && elem->get<bool>("selectorImageTile");
|
||||||
mSelectorImage.setImage(path, tile);
|
mSelectorImage.setImage(path, tile);
|
||||||
mSelectorImage.setSize(mSize.x(), mSelectorHeight);
|
mSelectorImage.setSize(mSize.x(), mSelectorHeight);
|
||||||
mSelectorImage.setColorShift(mSelectorColor);
|
mSelectorImage.setColorShift(mSelectorColor);
|
||||||
mSelectorImage.setColorShiftEnd(mSelectorColorEnd);
|
mSelectorImage.setColorShiftEnd(mSelectorColorEnd);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
mSelectorImage.setImage("");
|
mSelectorImage.setImage("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
#endif // ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VideoComponent.cpp
|
||||||
|
//
|
||||||
|
// Base class for playing videos.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/VideoComponent.h"
|
#include "components/VideoComponent.h"
|
||||||
|
|
||||||
#include "resources/ResourceManager.h"
|
#include "resources/ResourceManager.h"
|
||||||
|
@ -15,228 +21,230 @@
|
||||||
#define FADE_TIME_MS 200
|
#define FADE_TIME_MS 200
|
||||||
|
|
||||||
std::string getTitlePath() {
|
std::string getTitlePath() {
|
||||||
std::string titleFolder = getTitleFolder();
|
std::string titleFolder = getTitleFolder();
|
||||||
return titleFolder + "last_title.srt";
|
return titleFolder + "last_title.srt";
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string getTitleFolder() {
|
std::string getTitleFolder() {
|
||||||
std::string home = Utils::FileSystem::getHomePath();
|
std::string home = Utils::FileSystem::getHomePath();
|
||||||
return home + "/.emulationstation/tmp/";
|
return home + "/.emulationstation/tmp/";
|
||||||
}
|
}
|
||||||
|
|
||||||
void writeSubtitle(const char* gameName, const char* systemName, bool always)
|
void writeSubtitle(const char* gameName, const char* systemName, bool always)
|
||||||
{
|
{
|
||||||
FILE* file = fopen(getTitlePath().c_str(), "w");
|
FILE* file = fopen(getTitlePath().c_str(), "w");
|
||||||
int end = (int)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000));
|
int end = (int)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000));
|
||||||
if (always) {
|
|
||||||
fprintf(file, "1\n00:00:01,000 --> 00:00:%d,000\n", end);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(file, "1\n00:00:01,000 --> 00:00:08,000\n");
|
|
||||||
}
|
|
||||||
fprintf(file, "%s\n", gameName);
|
|
||||||
fprintf(file, "<i>%s</i>\n\n", systemName);
|
|
||||||
|
|
||||||
if (!always) {
|
if (always)
|
||||||
if (end > 12)
|
fprintf(file, "1\n00:00:01,000 --> 00:00:%d,000\n", end);
|
||||||
{
|
else
|
||||||
fprintf(file, "2\n00:00:%d,000 --> 00:00:%d,000\n%s\n<i>%s</i>\n", end-4, end, gameName, systemName);
|
fprintf(file, "1\n00:00:01,000 --> 00:00:08,000\n");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fflush(file);
|
fprintf(file, "%s\n", gameName);
|
||||||
fclose(file);
|
fprintf(file, "<i>%s</i>\n\n", systemName);
|
||||||
file = NULL;
|
|
||||||
|
if (!always) {
|
||||||
|
if (end > 12)
|
||||||
|
fprintf(file, "2\n00:00:%d,000 --> 00:00:%d,000\n%s\n<i>%s</i>\n",
|
||||||
|
end-4, end, gameName, systemName);
|
||||||
|
}
|
||||||
|
|
||||||
|
fflush(file);
|
||||||
|
fclose(file);
|
||||||
|
file = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setScreensaverMode(bool isScreensaver)
|
void VideoComponent::setScreensaverMode(bool isScreensaver)
|
||||||
{
|
{
|
||||||
mScreensaverMode = isScreensaver;
|
mScreensaverMode = isScreensaver;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoComponent::VideoComponent(Window* window) :
|
VideoComponent::VideoComponent(
|
||||||
GuiComponent(window),
|
Window* window)
|
||||||
mStaticImage(window),
|
: GuiComponent(window),
|
||||||
mVideoHeight(0),
|
mStaticImage(window),
|
||||||
mVideoWidth(0),
|
mVideoHeight(0),
|
||||||
mStartDelayed(false),
|
mVideoWidth(0),
|
||||||
mIsPlaying(false),
|
mStartDelayed(false),
|
||||||
mShowing(false),
|
mIsPlaying(false),
|
||||||
mScreensaverActive(false),
|
mShowing(false),
|
||||||
mDisable(false),
|
mScreensaverActive(false),
|
||||||
mScreensaverMode(false),
|
mDisable(false),
|
||||||
mTargetIsMax(false),
|
mScreensaverMode(false),
|
||||||
mTargetSize(0, 0)
|
mTargetIsMax(false),
|
||||||
|
mTargetSize(0, 0)
|
||||||
{
|
{
|
||||||
// Setup the default configuration
|
// Setup the default configuration.
|
||||||
mConfig.showSnapshotDelay = false;
|
mConfig.showSnapshotDelay = false;
|
||||||
mConfig.showSnapshotNoVideo = false;
|
mConfig.showSnapshotNoVideo = false;
|
||||||
mConfig.startDelay = 0;
|
mConfig.startDelay = 0;
|
||||||
if (mWindow->getGuiStackSize() > 1) {
|
|
||||||
topWindow(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string path = getTitleFolder();
|
if (mWindow->getGuiStackSize() > 1)
|
||||||
if(!Utils::FileSystem::exists(path))
|
topWindow(false);
|
||||||
Utils::FileSystem::createDirectory(path);
|
|
||||||
|
std::string path = getTitleFolder();
|
||||||
|
|
||||||
|
if (!Utils::FileSystem::exists(path))
|
||||||
|
Utils::FileSystem::createDirectory(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoComponent::~VideoComponent()
|
VideoComponent::~VideoComponent()
|
||||||
{
|
{
|
||||||
// Stop any currently running video
|
// Stop any currently running video.
|
||||||
stopVideo();
|
stopVideo();
|
||||||
// Delete subtitle file, if existing
|
// Delete subtitle file, if existing.
|
||||||
remove(getTitlePath().c_str());
|
remove(getTitlePath().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onOriginChanged()
|
void VideoComponent::onOriginChanged()
|
||||||
{
|
{
|
||||||
// Update the embeded static image
|
// Update the embeded static image.
|
||||||
mStaticImage.setOrigin(mOrigin);
|
mStaticImage.setOrigin(mOrigin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onPositionChanged()
|
void VideoComponent::onPositionChanged()
|
||||||
{
|
{
|
||||||
// Update the embeded static image
|
// Update the embeded static image.
|
||||||
mStaticImage.setPosition(mPosition);
|
mStaticImage.setPosition(mPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onSizeChanged()
|
void VideoComponent::onSizeChanged()
|
||||||
{
|
{
|
||||||
// Update the embeded static image
|
// Update the embeded static image.
|
||||||
mStaticImage.onSizeChanged();
|
mStaticImage.onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoComponent::setVideo(std::string path)
|
bool VideoComponent::setVideo(std::string path)
|
||||||
{
|
{
|
||||||
// Convert the path into a generic format
|
// Convert the path into a generic format.
|
||||||
std::string fullPath = Utils::FileSystem::getCanonicalPath(path);
|
std::string fullPath = Utils::FileSystem::getCanonicalPath(path);
|
||||||
|
|
||||||
// Check that it's changed
|
// Check that it's changed.
|
||||||
if (fullPath == mVideoPath)
|
if (fullPath == mVideoPath)
|
||||||
return !path.empty();
|
return !path.empty();
|
||||||
|
|
||||||
// Store the path
|
// Store the path.
|
||||||
mVideoPath = fullPath;
|
mVideoPath = fullPath;
|
||||||
|
|
||||||
// If the file exists then set the new video
|
// If the file exists then set the new video.
|
||||||
if (!fullPath.empty() && ResourceManager::getInstance()->fileExists(fullPath))
|
if (!fullPath.empty() && ResourceManager::getInstance()->fileExists(fullPath)) {
|
||||||
{
|
// Return true to show that we are going to attempt to play a video.
|
||||||
// Return true to show that we are going to attempt to play a video
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
|
||||||
// Return false to show that no video will be displayed
|
// Return false to show that no video will be displayed.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setImage(std::string path)
|
void VideoComponent::setImage(std::string path)
|
||||||
{
|
{
|
||||||
// Check that the image has changed
|
// Check that the image has changed.
|
||||||
if (path == mStaticImagePath)
|
if (path == mStaticImagePath)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
mStaticImage.setImage(path);
|
mStaticImage.setImage(path);
|
||||||
mFadeIn = 0.0f;
|
mFadeIn = 0.0f;
|
||||||
mStaticImagePath = path;
|
mStaticImagePath = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setDefaultVideo()
|
void VideoComponent::setDefaultVideo()
|
||||||
{
|
{
|
||||||
setVideo(mConfig.defaultVideoPath);
|
setVideo(mConfig.defaultVideoPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::setOpacity(unsigned char opacity)
|
void VideoComponent::setOpacity(unsigned char opacity)
|
||||||
{
|
{
|
||||||
mOpacity = opacity;
|
mOpacity = opacity;
|
||||||
// Update the embeded static image
|
// Update the embeded static image.
|
||||||
mStaticImage.setOpacity(opacity);
|
mStaticImage.setOpacity(opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::render(const Transform4x4f& parentTrans)
|
void VideoComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
|
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
// Handle the case where the video is delayed
|
// Handle the case where the video is delayed.
|
||||||
handleStartDelay();
|
handleStartDelay();
|
||||||
|
|
||||||
// Handle looping of the video
|
// Handle looping of the video.
|
||||||
handleLooping();
|
handleLooping();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans)
|
void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
// This is the case where the video is not currently being displayed. Work out
|
// This is the case where the video is not currently being displayed. Work out
|
||||||
// if we need to display a static image
|
// if we need to display a static image.
|
||||||
if ((mConfig.showSnapshotNoVideo && mVideoPath.empty()) || (mStartDelayed && mConfig.showSnapshotDelay))
|
if ((mConfig.showSnapshotNoVideo && mVideoPath.empty()) ||
|
||||||
{
|
(mStartDelayed && mConfig.showSnapshotDelay)) {
|
||||||
// Display the static image instead
|
// Display the static image instead.
|
||||||
mStaticImage.setOpacity((unsigned char)(mFadeIn * 255.0f));
|
mStaticImage.setOpacity((unsigned char)(mFadeIn * 255.0f));
|
||||||
mStaticImage.render(parentTrans);
|
mStaticImage.render(parentTrans);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
void VideoComponent::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 ^ SIZE) | ((properties & (SIZE | POSITION)) ? ORIGIN : 0));
|
GuiComponent::applyTheme(theme, view, element, (properties ^ SIZE) |
|
||||||
|
((properties & (SIZE | POSITION)) ? ORIGIN : 0));
|
||||||
|
|
||||||
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video");
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video");
|
||||||
if(!elem)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Vector2f scale = getParent() ? getParent()->getSize() : Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
if (!elem)
|
||||||
|
return;
|
||||||
|
|
||||||
if(properties & ThemeFlags::SIZE)
|
Vector2f scale = getParent() ? getParent()->getSize()
|
||||||
{
|
: Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
||||||
if(elem->has("size"))
|
|
||||||
setResize(elem->get<Vector2f>("size") * scale);
|
|
||||||
else if(elem->has("maxSize"))
|
|
||||||
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(elem->has("default"))
|
if (properties & ThemeFlags::SIZE) {
|
||||||
mConfig.defaultVideoPath = elem->get<std::string>("default");
|
if (elem->has("size"))
|
||||||
|
setResize(elem->get<Vector2f>("size") * scale);
|
||||||
|
else if (elem->has("maxSize"))
|
||||||
|
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
|
||||||
|
}
|
||||||
|
|
||||||
if((properties & ThemeFlags::DELAY) && elem->has("delay"))
|
if (elem->has("default"))
|
||||||
mConfig.startDelay = (unsigned)(elem->get<float>("delay") * 1000.0f);
|
mConfig.defaultVideoPath = elem->get<std::string>("default");
|
||||||
|
|
||||||
if (elem->has("showSnapshotNoVideo"))
|
if ((properties & ThemeFlags::DELAY) && elem->has("delay"))
|
||||||
mConfig.showSnapshotNoVideo = elem->get<bool>("showSnapshotNoVideo");
|
mConfig.startDelay = (unsigned)(elem->get<float>("delay") * 1000.0f);
|
||||||
|
|
||||||
if (elem->has("showSnapshotDelay"))
|
if (elem->has("showSnapshotNoVideo"))
|
||||||
mConfig.showSnapshotDelay = elem->get<bool>("showSnapshotDelay");
|
mConfig.showSnapshotNoVideo = elem->get<bool>("showSnapshotNoVideo");
|
||||||
|
|
||||||
|
if (elem->has("showSnapshotDelay"))
|
||||||
|
mConfig.showSnapshotDelay = elem->get<bool>("showSnapshotDelay");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<HelpPrompt> VideoComponent::getHelpPrompts()
|
std::vector<HelpPrompt> VideoComponent::getHelpPrompts()
|
||||||
{
|
{
|
||||||
std::vector<HelpPrompt> ret;
|
std::vector<HelpPrompt> ret;
|
||||||
ret.push_back(HelpPrompt("a", "select"));
|
ret.push_back(HelpPrompt("a", "select"));
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::handleStartDelay()
|
void VideoComponent::handleStartDelay()
|
||||||
{
|
{
|
||||||
// Only play if any delay has timed out
|
// Only play if any delay has timed out.
|
||||||
if (mStartDelayed)
|
if (mStartDelayed) {
|
||||||
{
|
if (mStartTime > SDL_GetTicks()) {
|
||||||
if (mStartTime > SDL_GetTicks())
|
// Timeout not yet completed.
|
||||||
{
|
return;
|
||||||
// Timeout not yet completed
|
}
|
||||||
return;
|
// Completed.
|
||||||
}
|
mStartDelayed = false;
|
||||||
// Completed
|
// Clear the playing flag so startVideo works.
|
||||||
mStartDelayed = false;
|
mIsPlaying = false;
|
||||||
// Clear the playing flag so startVideo works
|
startVideo();
|
||||||
mIsPlaying = false;
|
}
|
||||||
startVideo();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::handleLooping()
|
void VideoComponent::handleLooping()
|
||||||
|
@ -245,119 +253,105 @@ void VideoComponent::handleLooping()
|
||||||
|
|
||||||
void VideoComponent::startVideoWithDelay()
|
void VideoComponent::startVideoWithDelay()
|
||||||
{
|
{
|
||||||
// If not playing then either start the video or initiate the delay
|
// If not playing then either start the video or initiate the delay.
|
||||||
if (!mIsPlaying)
|
if (!mIsPlaying) {
|
||||||
{
|
// 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
|
mPlayingVideoPath = mVideoPath;
|
||||||
mPlayingVideoPath = mVideoPath;
|
|
||||||
|
|
||||||
if (mConfig.startDelay == 0 || PowerSaver::getMode() == PowerSaver::INSTANT)
|
if (mConfig.startDelay == 0 || PowerSaver::getMode() == PowerSaver::INSTANT) {
|
||||||
{
|
// No delay. Just start the video.
|
||||||
// No delay. Just start the video
|
mStartDelayed = false;
|
||||||
mStartDelayed = false;
|
startVideo();
|
||||||
startVideo();
|
}
|
||||||
}
|
else {
|
||||||
else
|
// Configure the start delay.
|
||||||
{
|
mStartDelayed = true;
|
||||||
// Configure the start delay
|
mFadeIn = 0.0f;
|
||||||
mStartDelayed = true;
|
mStartTime = SDL_GetTicks() + mConfig.startDelay;
|
||||||
mFadeIn = 0.0f;
|
}
|
||||||
mStartTime = SDL_GetTicks() + mConfig.startDelay;
|
mIsPlaying = true;
|
||||||
}
|
}
|
||||||
mIsPlaying = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::update(int deltaTime)
|
void VideoComponent::update(int deltaTime)
|
||||||
{
|
{
|
||||||
manageState();
|
manageState();
|
||||||
|
|
||||||
// If the video start is delayed and there is less than the fade time then set the image fade
|
// If the video start is delayed and there is less than the fade time, then set
|
||||||
// accordingly
|
// the image fade accordingly.
|
||||||
if (mStartDelayed)
|
if (mStartDelayed) {
|
||||||
{
|
Uint32 ticks = SDL_GetTicks();
|
||||||
Uint32 ticks = SDL_GetTicks();
|
if (mStartTime > ticks) {
|
||||||
if (mStartTime > ticks)
|
Uint32 diff = mStartTime - ticks;
|
||||||
{
|
if (diff < FADE_TIME_MS) {
|
||||||
Uint32 diff = mStartTime - ticks;
|
mFadeIn = (float)diff / (float)FADE_TIME_MS;
|
||||||
if (diff < FADE_TIME_MS)
|
return;
|
||||||
{
|
}
|
||||||
mFadeIn = (float)diff / (float)FADE_TIME_MS;
|
}
|
||||||
return;
|
}
|
||||||
}
|
// If the fade in is less than 1 then increment it.
|
||||||
}
|
if (mFadeIn < 1.0f) {
|
||||||
}
|
mFadeIn += deltaTime / (float)FADE_TIME_MS;
|
||||||
// If the fade in is less than 1 then increment it
|
if (mFadeIn > 1.0f)
|
||||||
if (mFadeIn < 1.0f)
|
mFadeIn = 1.0f;
|
||||||
{
|
}
|
||||||
mFadeIn += deltaTime / (float)FADE_TIME_MS;
|
GuiComponent::update(deltaTime);
|
||||||
if (mFadeIn > 1.0f)
|
|
||||||
mFadeIn = 1.0f;
|
|
||||||
}
|
|
||||||
GuiComponent::update(deltaTime);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::manageState()
|
void VideoComponent::manageState()
|
||||||
{
|
{
|
||||||
// We will only show if the component is on display and the screensaver
|
// We will only show if the component is on display and the screensaver
|
||||||
// is not active
|
// is not active.
|
||||||
bool show = mShowing && !mScreensaverActive && !mDisable;
|
bool show = mShowing && !mScreensaverActive && !mDisable;
|
||||||
|
|
||||||
// See if we're already playing
|
// See if we're already playing.
|
||||||
if (mIsPlaying)
|
if (mIsPlaying) {
|
||||||
{
|
// If we are not on display then stop the video from playing.
|
||||||
// If we are not on display then stop the video from playing
|
if (!show) {
|
||||||
if (!show)
|
stopVideo();
|
||||||
{
|
}
|
||||||
stopVideo();
|
else {
|
||||||
}
|
if (mVideoPath != mPlayingVideoPath) {
|
||||||
else
|
// Path changed. Stop the video. We will start it again below because
|
||||||
{
|
// mIsPlaying will be modified by stopVideo to be false.
|
||||||
if (mVideoPath != mPlayingVideoPath)
|
stopVideo();
|
||||||
{
|
}
|
||||||
// Path changed. Stop the video. We will start it again below because
|
}
|
||||||
// mIsPlaying will be modified by stopVideo to be false
|
}
|
||||||
stopVideo();
|
// Need to recheck variable rather than 'else' because it may be modified above.
|
||||||
}
|
if (!mIsPlaying) {
|
||||||
}
|
// If we are on display then see if we should start the video.
|
||||||
}
|
if (show && !mVideoPath.empty())
|
||||||
// Need to recheck variable rather than 'else' because it may be modified above
|
startVideoWithDelay();
|
||||||
if (!mIsPlaying)
|
}
|
||||||
{
|
|
||||||
// If we are on display then see if we should start the video
|
|
||||||
if (show && !mVideoPath.empty())
|
|
||||||
{
|
|
||||||
startVideoWithDelay();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onShow()
|
void VideoComponent::onShow()
|
||||||
{
|
{
|
||||||
mShowing = true;
|
mShowing = true;
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onHide()
|
void VideoComponent::onHide()
|
||||||
{
|
{
|
||||||
mShowing = false;
|
mShowing = false;
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onScreenSaverActivate()
|
void VideoComponent::onScreenSaverActivate()
|
||||||
{
|
{
|
||||||
mScreensaverActive = true;
|
mScreensaverActive = true;
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::onScreenSaverDeactivate()
|
void VideoComponent::onScreenSaverDeactivate()
|
||||||
{
|
{
|
||||||
mScreensaverActive = false;
|
mScreensaverActive = false;
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::topWindow(bool isTop)
|
void VideoComponent::topWindow(bool isTop)
|
||||||
{
|
{
|
||||||
mDisable = !isTop;
|
mDisable = !isTop;
|
||||||
manageState();
|
manageState();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
//
|
||||||
|
// VideoComponent.h
|
||||||
|
//
|
||||||
|
// Base class for playing videos.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#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 "components/ImageComponent.h"
|
||||||
#include "GuiComponent.h"
|
#include "GuiComponent.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class TextureResource;
|
class TextureResource;
|
||||||
|
@ -14,101 +21,101 @@ void writeSubtitle(const char* gameName, const char* systemName, bool always);
|
||||||
|
|
||||||
class VideoComponent : public GuiComponent
|
class VideoComponent : public GuiComponent
|
||||||
{
|
{
|
||||||
// Structure that groups together the configuration of the video component
|
// Structure that groups together the configuration of the video component.
|
||||||
struct Configuration
|
struct Configuration {
|
||||||
{
|
unsigned startDelay;
|
||||||
unsigned startDelay;
|
bool showSnapshotNoVideo;
|
||||||
bool showSnapshotNoVideo;
|
bool showSnapshotDelay;
|
||||||
bool showSnapshotDelay;
|
std::string defaultVideoPath;
|
||||||
std::string defaultVideoPath;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VideoComponent(Window* window);
|
VideoComponent(Window* window);
|
||||||
virtual ~VideoComponent();
|
virtual ~VideoComponent();
|
||||||
|
|
||||||
// Loads the video at the given filepath
|
// Loads the video at the given filepath.
|
||||||
bool setVideo(std::string path);
|
bool setVideo(std::string path);
|
||||||
// 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);
|
||||||
|
|
||||||
// Configures the component to show the default video
|
// Configures the component to show the default video.
|
||||||
void setDefaultVideo();
|
void setDefaultVideo();
|
||||||
|
|
||||||
// sets whether it's going to render in screensaver mode
|
// Sets whether it's going to render in screensaver mode.
|
||||||
void setScreensaverMode(bool isScreensaver);
|
void setScreensaverMode(bool isScreensaver);
|
||||||
|
|
||||||
virtual void onShow() override;
|
virtual void onShow() override;
|
||||||
virtual void onHide() override;
|
virtual void onHide() override;
|
||||||
virtual void onScreenSaverActivate() override;
|
virtual void onScreenSaverActivate() override;
|
||||||
virtual void onScreenSaverDeactivate() override;
|
virtual void onScreenSaverDeactivate() override;
|
||||||
virtual void topWindow(bool isTop) override;
|
virtual void topWindow(bool isTop) override;
|
||||||
|
|
||||||
void onOriginChanged() override;
|
void onOriginChanged() override;
|
||||||
void onPositionChanged() override;
|
void onPositionChanged() override;
|
||||||
void onSizeChanged() override;
|
void onSizeChanged() override;
|
||||||
void setOpacity(unsigned char opacity) override;
|
void setOpacity(unsigned char opacity) override;
|
||||||
|
|
||||||
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, const std::string& element, unsigned int properties) override;
|
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
|
||||||
|
const std::string& element, unsigned int properties) override;
|
||||||
|
|
||||||
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
virtual std::vector<HelpPrompt> getHelpPrompts() override;
|
||||||
|
|
||||||
virtual void update(int deltaTime) override;
|
virtual void update(int deltaTime) override;
|
||||||
|
|
||||||
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain aspect ratio.
|
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain
|
||||||
// If both are non-zero, potentially break the aspect ratio. If both are zero, no resizing.
|
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
||||||
// 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) = 0;
|
virtual void setResize(float width, float height) = 0;
|
||||||
inline void setResize(const Vector2f& size) { setResize(size.x(), size.y()); }
|
inline 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.
|
||||||
// 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()); }
|
inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Start the video Immediately
|
// Start the video immediately.
|
||||||
virtual void startVideo() = 0;
|
virtual void startVideo() = 0;
|
||||||
// Stop the video
|
// Stop the video.
|
||||||
virtual void stopVideo() { };
|
virtual void stopVideo() { };
|
||||||
// Handle looping the video. Must be called periodically
|
// Handle looping the video. Must be called periodically.
|
||||||
virtual void handleLooping();
|
virtual void handleLooping();
|
||||||
|
|
||||||
// Start the video after any configured delay
|
// Start the video after any configured delay.
|
||||||
void startVideoWithDelay();
|
void startVideoWithDelay();
|
||||||
|
|
||||||
// Handle any delay to the start of playing the video clip. Must be called periodically
|
// Handle any delay to the start of playing the video clip. Must be called periodically.
|
||||||
void handleStartDelay();
|
void handleStartDelay();
|
||||||
|
|
||||||
// Manage the playing state of the component
|
// Manage the playing state of the component.
|
||||||
void manageState();
|
void manageState();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
unsigned mVideoWidth;
|
unsigned mVideoWidth;
|
||||||
unsigned mVideoHeight;
|
unsigned mVideoHeight;
|
||||||
Vector2f mTargetSize;
|
Vector2f mTargetSize;
|
||||||
std::shared_ptr<TextureResource> mTexture;
|
std::shared_ptr<TextureResource> mTexture;
|
||||||
float mFadeIn;
|
float mFadeIn;
|
||||||
std::string mStaticImagePath;
|
std::string mStaticImagePath;
|
||||||
ImageComponent mStaticImage;
|
ImageComponent mStaticImage;
|
||||||
|
|
||||||
std::string mVideoPath;
|
std::string mVideoPath;
|
||||||
std::string mPlayingVideoPath;
|
std::string mPlayingVideoPath;
|
||||||
bool mStartDelayed;
|
bool mStartDelayed;
|
||||||
unsigned mStartTime;
|
unsigned mStartTime;
|
||||||
bool mIsPlaying;
|
bool mIsPlaying;
|
||||||
bool mShowing;
|
bool mShowing;
|
||||||
bool mDisable;
|
bool mDisable;
|
||||||
bool mScreensaverActive;
|
bool mScreensaverActive;
|
||||||
bool mScreensaverMode;
|
bool mScreensaverMode;
|
||||||
bool mTargetIsMax;
|
bool mTargetIsMax;
|
||||||
|
|
||||||
Configuration mConfig;
|
Configuration mConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VideoPlayerComponent.cpp
|
||||||
|
//
|
||||||
|
// OMXPlayer video playing for Raspberry Pi.
|
||||||
|
//
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
#include "components/VideoPlayerComponent.h"
|
#include "components/VideoPlayerComponent.h"
|
||||||
|
|
||||||
|
@ -5,6 +11,7 @@
|
||||||
#include "utils/StringUtil.h"
|
#include "utils/StringUtil.h"
|
||||||
#include "AudioManager.h"
|
#include "AudioManager.h"
|
||||||
#include "Settings.h"
|
#include "Settings.h"
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <wait.h>
|
#include <wait.h>
|
||||||
|
@ -12,250 +19,235 @@
|
||||||
class VolumeControl
|
class VolumeControl
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static std::shared_ptr<VolumeControl> & getInstance();
|
static std::shared_ptr<VolumeControl> & getInstance();
|
||||||
int getVolume() const;
|
int getVolume() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
VideoPlayerComponent::VideoPlayerComponent(Window* window, std::string path) :
|
VideoPlayerComponent::VideoPlayerComponent(Window* window, std::string path) :
|
||||||
VideoComponent(window),
|
VideoComponent(window),
|
||||||
mPlayerPid(-1),
|
mPlayerPid(-1),
|
||||||
subtitlePath(path)
|
subtitlePath(path)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoPlayerComponent::~VideoPlayerComponent()
|
VideoPlayerComponent::~VideoPlayerComponent()
|
||||||
{
|
{
|
||||||
stopVideo();
|
stopVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayerComponent::render(const Transform4x4f& parentTrans)
|
void VideoPlayerComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VideoComponent::render(parentTrans);
|
VideoComponent::render(parentTrans);
|
||||||
|
|
||||||
if (!mIsPlaying || mPlayerPid == -1)
|
if (!mIsPlaying || mPlayerPid == -1)
|
||||||
VideoComponent::renderSnapshot(parentTrans);
|
VideoComponent::renderSnapshot(parentTrans);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayerComponent::setResize(float width, float height)
|
void VideoPlayerComponent::setResize(float width, float height)
|
||||||
{
|
{
|
||||||
setSize(width, height);
|
setSize(width, height);
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
mStaticImage.setResize(width, height);
|
mStaticImage.setResize(width, height);
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayerComponent::setMaxSize(float width, float height)
|
void VideoPlayerComponent::setMaxSize(float width, float height)
|
||||||
{
|
{
|
||||||
setSize(width, height);
|
setSize(width, height);
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = true;
|
mTargetIsMax = true;
|
||||||
mStaticImage.setMaxSize(width, height);
|
mStaticImage.setMaxSize(width, height);
|
||||||
onSizeChanged();
|
onSizeChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayerComponent::startVideo()
|
void VideoPlayerComponent::startVideo()
|
||||||
{
|
{
|
||||||
if (!mIsPlaying)
|
if (!mIsPlaying) {
|
||||||
{
|
mVideoWidth = 0;
|
||||||
mVideoWidth = 0;
|
mVideoHeight = 0;
|
||||||
mVideoHeight = 0;
|
|
||||||
|
|
||||||
std::string path(mVideoPath.c_str());
|
std::string path(mVideoPath.c_str());
|
||||||
|
|
||||||
// Make sure we have a video path
|
// Make sure we have a video path.
|
||||||
if ((path.size() > 0) && (mPlayerPid == -1))
|
if ((path.size() > 0) && (mPlayerPid == -1)) {
|
||||||
{
|
// 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
|
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()->getString("OMXAudioDev").c_str(), "alsa"))
|
if (Utils::String::startsWith(Settings::getInstance()->
|
||||||
{
|
getString("OMXAudioDev").c_str(), "alsa"))
|
||||||
AudioManager::getInstance()->deinit();
|
AudioManager::getInstance()->deinit();
|
||||||
}
|
|
||||||
|
|
||||||
// Start the player process
|
// Start the player process.
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid == -1)
|
if (pid == -1) {
|
||||||
{
|
// Failed.
|
||||||
// Failed
|
mPlayingVideoPath = "";
|
||||||
mPlayingVideoPath = "";
|
}
|
||||||
}
|
else if (pid > 0) {
|
||||||
else if (pid > 0)
|
mPlayerPid = pid;
|
||||||
{
|
// Update the playing state.
|
||||||
mPlayerPid = pid;
|
signal(SIGCHLD, catch_child);
|
||||||
// Update the playing state
|
mIsPlaying = true;
|
||||||
signal(SIGCHLD, catch_child);
|
mFadeIn = 0.0f;
|
||||||
mIsPlaying = true;
|
}
|
||||||
mFadeIn = 0.0f;
|
else {
|
||||||
}
|
// Find out the pixel position of the video view and build a command line for
|
||||||
else
|
// OMXPlayer to position it in the right place.
|
||||||
{
|
char buf1[32];
|
||||||
|
char buf2[32];
|
||||||
|
float x = mPosition.x() - (mOrigin.x() * mSize.x());
|
||||||
|
float y = mPosition.y() - (mOrigin.y() * mSize.y());
|
||||||
|
|
||||||
// Find out the pixel position of the video view and build a command line for
|
// Fix x and y.
|
||||||
// omxplayer to position it in the right place
|
switch (Renderer::getScreenRotate()) {
|
||||||
char buf1[32];
|
case 0: {
|
||||||
char buf2[32];
|
const int x1 = (int)(Renderer::getScreenOffsetX() + x);
|
||||||
float x = mPosition.x() - (mOrigin.x() * mSize.x());
|
const int y1 = (int)(Renderer::getScreenOffsetY() + y);
|
||||||
float y = mPosition.y() - (mOrigin.y() * mSize.y());
|
const int x2 = (int)(x1 + mSize.x());
|
||||||
|
const int y2 = (int)(y1 + mSize.y());
|
||||||
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// fix x and y
|
case 1: {
|
||||||
switch(Renderer::getScreenRotate())
|
const int x1 = (int)(Renderer::getWindowWidth() -
|
||||||
{
|
Renderer::getScreenOffsetY() - y - mSize.y());
|
||||||
case 0:
|
const int y1 = (int)(Renderer::getScreenOffsetX() + x);
|
||||||
{
|
const int x2 = (int)(x1 + mSize.y());
|
||||||
const int x1 = (int)(Renderer::getScreenOffsetX() + x);
|
const int y2 = (int)(y1 + mSize.x());
|
||||||
const int y1 = (int)(Renderer::getScreenOffsetY() + y);
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
const int x2 = (int)(x1 + mSize.x());
|
}
|
||||||
const int y2 = (int)(y1 + mSize.y());
|
break;
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
case 2: {
|
||||||
{
|
const int x1 = (int)(Renderer::getWindowWidth() -
|
||||||
const int x1 = (int)(Renderer::getWindowWidth() - Renderer::getScreenOffsetY() - y - mSize.y());
|
Renderer::getScreenOffsetX() - x - mSize.x());
|
||||||
const int y1 = (int)(Renderer::getScreenOffsetX() + x);
|
const int y1 = (int)(Renderer::getWindowHeight() -
|
||||||
const int x2 = (int)(x1 + mSize.y());
|
Renderer::getScreenOffsetY() - y - mSize.y());
|
||||||
const int y2 = (int)(y1 + mSize.x());
|
const int x2 = (int)(x1 + mSize.x());
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
const int y2 = (int)(y1 + mSize.y());
|
||||||
}
|
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
||||||
break;
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 2:
|
case 3: {
|
||||||
{
|
const int x1 = (int)(Renderer::getScreenOffsetY() + y);
|
||||||
const int x1 = (int)(Renderer::getWindowWidth() - Renderer::getScreenOffsetX() - x - mSize.x());
|
const int y1 = (int)(Renderer::getWindowHeight() -
|
||||||
const int y1 = (int)(Renderer::getWindowHeight() - Renderer::getScreenOffsetY() - y - mSize.y());
|
Renderer::getScreenOffsetX() - x - mSize.x());
|
||||||
const int x2 = (int)(x1 + mSize.x());
|
const int x2 = (int)(x1 + mSize.y());
|
||||||
const int y2 = (int)(y1 + mSize.y());
|
const int y2 = (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 3:
|
// Rotate the video.
|
||||||
{
|
switch (Renderer::getScreenRotate()) {
|
||||||
const int x1 = (int)(Renderer::getScreenOffsetY() + y);
|
case 0: { sprintf(buf2, "%d", (int) 0); } break;
|
||||||
const int y1 = (int)(Renderer::getWindowHeight() - Renderer::getScreenOffsetX() - x - mSize.x());
|
case 1: { sprintf(buf2, "%d", (int) 90); } break;
|
||||||
const int x2 = (int)(x1 + mSize.y());
|
case 2: { sprintf(buf2, "%d", (int)180); } break;
|
||||||
const int y2 = (int)(y1 + mSize.x());
|
case 3: { sprintf(buf2, "%d", (int)270); } break;
|
||||||
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
|
}
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// rotate the video
|
// We need to specify the layer of 10000 or above to ensure the video is
|
||||||
switch(Renderer::getScreenRotate())
|
// displayed on top of our SDL display.
|
||||||
{
|
const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd",
|
||||||
case 0: { sprintf(buf2, "%d", (int) 0); } break;
|
"--aspect-mode", "letterbox", "--vol", "0", "-o", "both",
|
||||||
case 1: { sprintf(buf2, "%d", (int) 90); } break;
|
"--win", buf1, "--orientation", buf2, "", "", "", "", "", "",
|
||||||
case 2: { sprintf(buf2, "%d", (int)180); } break;
|
"", "", "", "", "", NULL };
|
||||||
case 3: { sprintf(buf2, "%d", (int)270); } break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to specify the layer of 10000 or above to ensure the video is displayed on top
|
// Check if we want to mute the audio.
|
||||||
// of our SDL display
|
if ((!Settings::getInstance()->getBool("VideoAudio") ||
|
||||||
|
(float)VolumeControl::getInstance()->getVolume() == 0) ||
|
||||||
|
(Settings::getInstance()->getBool("ScreenSaverVideoMute") &&
|
||||||
|
mScreensaverMode)) {
|
||||||
|
argv[8] = "-1000000";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float percentVolume = (float)VolumeControl::getInstance()->getVolume();
|
||||||
|
int OMXVolume = (int)((percentVolume-98)*105);
|
||||||
|
argv[8] = std::to_string(OMXVolume).c_str();
|
||||||
|
}
|
||||||
|
|
||||||
const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", "--aspect-mode", "letterbox", "--vol", "0", "-o", "both","--win", buf1, "--orientation", buf2, "", "", "", "", "", "", "", "", "", "", "", NULL };
|
// Test if there's a path for possible subtitles, meaning we're a screensaver video.
|
||||||
|
if (!subtitlePath.empty()) {
|
||||||
|
// If we are rendering a screensaver.
|
||||||
|
// Check if we want to stretch the image.
|
||||||
|
if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver"))
|
||||||
|
argv[6] = "stretch";
|
||||||
|
|
||||||
// check if we want to mute the audio
|
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") {
|
||||||
if ((!Settings::getInstance()->getBool("VideoAudio") || (float)VolumeControl::getInstance()->getVolume() == 0) ||
|
// If we have chosen to render subtitles.
|
||||||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
|
argv[15] = "--subtitles";
|
||||||
{
|
argv[16] = subtitlePath.c_str();
|
||||||
argv[8] = "-1000000";
|
argv[17] = mPlayingVideoPath.c_str();
|
||||||
}
|
argv[18] = "--font";
|
||||||
else
|
argv[19] = Settings::getInstance()->getString("SubtitleFont").c_str();
|
||||||
{
|
argv[20] = "--italic-font";
|
||||||
float percentVolume = (float)VolumeControl::getInstance()->getVolume();
|
argv[21] = Settings::getInstance()->
|
||||||
int OMXVolume = (int)((percentVolume-98)*105);
|
getString("SubtitleItalicFont").c_str();
|
||||||
argv[8] = std::to_string(OMXVolume).c_str();
|
argv[22] = "--font-size";
|
||||||
}
|
argv[23] = std::to_string(Settings::getInstance()->
|
||||||
|
getInt("SubtitleSize")).c_str();
|
||||||
|
argv[24] = "--align";
|
||||||
|
argv[25] = Settings::getInstance()->
|
||||||
|
getString("SubtitleAlignment").c_str();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If we have chosen NOT to render subtitles in the screensaver.
|
||||||
|
argv[15] = mPlayingVideoPath.c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// If we are rendering a video gamelist.
|
||||||
|
if (!mTargetIsMax)
|
||||||
|
argv[6] = "stretch";
|
||||||
|
argv[15] = mPlayingVideoPath.c_str();
|
||||||
|
}
|
||||||
|
|
||||||
// test if there's a path for possible subtitles, meaning we're a screensaver video
|
argv[10] = Settings::getInstance()->getString("OMXAudioDev").c_str();
|
||||||
if (!subtitlePath.empty())
|
|
||||||
{
|
|
||||||
// if we are rendering a screensaver
|
|
||||||
|
|
||||||
// check if we want to stretch the image
|
//const char* argv[] = args;
|
||||||
if (Settings::getInstance()->getBool("StretchVideoOnScreenSaver"))
|
const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL };
|
||||||
{
|
|
||||||
argv[6] = "stretch";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never")
|
// Redirect stdout.
|
||||||
{
|
int fdin = open("/dev/null", O_RDONLY);
|
||||||
// if we have chosen to render subtitles
|
int fdout = open("/dev/null", O_WRONLY);
|
||||||
argv[15] = "--subtitles";
|
dup2(fdin, 0);
|
||||||
argv[16] = subtitlePath.c_str();
|
dup2(fdout, 1);
|
||||||
argv[17] = mPlayingVideoPath.c_str();
|
// Run the OMXPlayer binary.
|
||||||
argv[18] = "--font";
|
execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env);
|
||||||
argv[19] = Settings::getInstance()->getString("SubtitleFont").c_str();
|
|
||||||
argv[20] = "--italic-font";
|
|
||||||
argv[21] = Settings::getInstance()->getString("SubtitleItalicFont").c_str();
|
|
||||||
argv[22] = "--font-size";
|
|
||||||
argv[23] = std::to_string(Settings::getInstance()->getInt("SubtitleSize")).c_str();
|
|
||||||
argv[24] = "--align";
|
|
||||||
argv[25] = Settings::getInstance()->getString("SubtitleAlignment").c_str();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if we have chosen NOT to render subtitles in the screensaver
|
|
||||||
argv[15] = mPlayingVideoPath.c_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// if we are rendering a video gamelist
|
|
||||||
if (!mTargetIsMax)
|
|
||||||
{
|
|
||||||
argv[6] = "stretch";
|
|
||||||
}
|
|
||||||
argv[15] = mPlayingVideoPath.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
argv[10] = Settings::getInstance()->getString("OMXAudioDev").c_str();
|
_exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
//const char* argv[] = args;
|
}
|
||||||
const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL };
|
}
|
||||||
|
|
||||||
// Redirect stdout
|
|
||||||
int fdin = open("/dev/null", O_RDONLY);
|
|
||||||
int fdout = open("/dev/null", O_WRONLY);
|
|
||||||
dup2(fdin, 0);
|
|
||||||
dup2(fdout, 1);
|
|
||||||
// Run the omxplayer binary
|
|
||||||
execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env);
|
|
||||||
|
|
||||||
_exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void catch_child(int sig_num)
|
void catch_child(int sig_num)
|
||||||
{
|
{
|
||||||
/* when we get here, we know there's a zombie child waiting */
|
// When we get here, we know there's a zombie child waiting.
|
||||||
int child_status;
|
int child_status;
|
||||||
wait(&child_status);
|
wait(&child_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoPlayerComponent::stopVideo()
|
void VideoPlayerComponent::stopVideo()
|
||||||
{
|
{
|
||||||
mIsPlaying = false;
|
mIsPlaying = false;
|
||||||
mStartDelayed = false;
|
mStartDelayed = false;
|
||||||
|
|
||||||
// Stop the player process
|
// Stop the player process.
|
||||||
if (mPlayerPid != -1)
|
if (mPlayerPid != -1) {
|
||||||
{
|
int status;
|
||||||
int status;
|
kill(mPlayerPid, SIGKILL);
|
||||||
kill(mPlayerPid, SIGKILL);
|
waitpid(mPlayerPid, &status, WNOHANG);
|
||||||
waitpid(mPlayerPid, &status, WNOHANG);
|
mPlayerPid = -1;
|
||||||
mPlayerPid = -1;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VideoPlayerComponent.h
|
||||||
|
//
|
||||||
|
// OMXPlayer video playing for Raspberry Pi.
|
||||||
|
//
|
||||||
|
|
||||||
#ifdef _RPI_
|
#ifdef _RPI_
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_VIDEO_PLAYER_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_VIDEO_PLAYER_COMPONENT_H
|
||||||
|
@ -10,31 +16,31 @@ void catch_child(int sig_num);
|
||||||
class VideoPlayerComponent : public VideoComponent
|
class VideoPlayerComponent : public VideoComponent
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
VideoPlayerComponent(Window* window, std::string path);
|
VideoPlayerComponent(Window* window, std::string path);
|
||||||
virtual ~VideoPlayerComponent();
|
virtual ~VideoPlayerComponent();
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain aspect ratio.
|
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain
|
||||||
// If both are non-zero, potentially break the aspect ratio. If both are zero, no resizing.
|
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
|
||||||
// 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.
|
||||||
void setResize(float width, float height);
|
void setResize(float width, float height) override;
|
||||||
|
|
||||||
// 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.
|
||||||
// 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.
|
||||||
void setMaxSize(float width, float height);
|
void setMaxSize(float width, float height) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Start the video Immediately
|
// Start the video Immediately.
|
||||||
virtual void startVideo();
|
virtual void startVideo() override;
|
||||||
// Stop the video
|
// Stop the video.
|
||||||
virtual void stopVideo();
|
virtual void stopVideo() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
pid_t mPlayerPid;
|
pid_t mPlayerPid;
|
||||||
std::string subtitlePath;
|
std::string subtitlePath;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_VIDEO_PLAYER_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_VIDEO_PLAYER_COMPONENT_H
|
||||||
|
|
|
@ -1,3 +1,9 @@
|
||||||
|
//
|
||||||
|
// VideoVlcComponent.cpp
|
||||||
|
//
|
||||||
|
// Video playing using libVLC.
|
||||||
|
//
|
||||||
|
|
||||||
#include "components/VideoVlcComponent.h"
|
#include "components/VideoVlcComponent.h"
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
|
@ -20,337 +26,322 @@
|
||||||
#include <codecvt>
|
#include <codecvt>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
libvlc_instance_t* VideoVlcComponent::mVLC = NULL;
|
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
|
||||||
|
|
||||||
// VLC prepares to render a video frame.
|
// VLC prepares to render a video frame.
|
||||||
static void *lock(void *data, void **p_pixels) {
|
static void* lock(void* data, void** p_pixels) {
|
||||||
struct VideoContext *c = (struct VideoContext *)data;
|
struct VideoContext* c = (struct VideoContext*)data;
|
||||||
SDL_LockMutex(c->mutex);
|
SDL_LockMutex(c->mutex);
|
||||||
SDL_LockSurface(c->surface);
|
SDL_LockSurface(c->surface);
|
||||||
*p_pixels = c->surface->pixels;
|
*p_pixels = c->surface->pixels;
|
||||||
return NULL; // Picture identifier, not needed here.
|
return nullptr; // Picture identifier, not needed here.
|
||||||
}
|
}
|
||||||
|
|
||||||
// VLC just rendered a video frame.
|
// VLC just rendered a video frame.
|
||||||
static void unlock(void *data, void* /*id*/, void *const* /*p_pixels*/) {
|
static void unlock(void* data, void* /*id*/, void *const* /*p_pixels*/) {
|
||||||
struct VideoContext *c = (struct VideoContext *)data;
|
struct VideoContext* c = (struct VideoContext*)data;
|
||||||
SDL_UnlockSurface(c->surface);
|
SDL_UnlockSurface(c->surface);
|
||||||
SDL_UnlockMutex(c->mutex);
|
SDL_UnlockMutex(c->mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// VLC wants to display a video frame.
|
// VLC wants to display a video frame.
|
||||||
static void display(void* /*data*/, void* /*id*/) {
|
static void display(void* /*data*/, void* /*id*/) {
|
||||||
//Data to be displayed
|
// Data to be displayed.
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles) :
|
VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles)
|
||||||
VideoComponent(window),
|
: VideoComponent(window), mMediaPlayer(nullptr)
|
||||||
mMediaPlayer(nullptr)
|
|
||||||
{
|
{
|
||||||
memset(&mContext, 0, sizeof(mContext));
|
memset(&mContext, 0, sizeof(mContext));
|
||||||
|
|
||||||
// Get an empty texture for rendering the video
|
// Get an empty texture for rendering the video.
|
||||||
mTexture = TextureResource::get("");
|
mTexture = TextureResource::get("");
|
||||||
|
|
||||||
// Make sure VLC has been initialised
|
// Make sure VLC has been initialized.
|
||||||
setupVLC(subtitles);
|
setupVLC(subtitles);
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoVlcComponent::~VideoVlcComponent()
|
VideoVlcComponent::~VideoVlcComponent()
|
||||||
{
|
{
|
||||||
stopVideo();
|
stopVideo();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::setResize(float width, float height)
|
void VideoVlcComponent::setResize(float width, float height)
|
||||||
{
|
{
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = false;
|
mTargetIsMax = false;
|
||||||
mStaticImage.setResize(width, height);
|
mStaticImage.setResize(width, height);
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::setMaxSize(float width, float height)
|
void VideoVlcComponent::setMaxSize(float width, float height)
|
||||||
{
|
{
|
||||||
mTargetSize = Vector2f(width, height);
|
mTargetSize = Vector2f(width, height);
|
||||||
mTargetIsMax = true;
|
mTargetIsMax = true;
|
||||||
mStaticImage.setMaxSize(width, height);
|
mStaticImage.setMaxSize(width, height);
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::resize()
|
void VideoVlcComponent::resize()
|
||||||
{
|
{
|
||||||
if(!mTexture)
|
if (!mTexture)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const Vector2f textureSize((float)mVideoWidth, (float)mVideoHeight);
|
const Vector2f textureSize((float)mVideoWidth, (float)mVideoHeight);
|
||||||
|
|
||||||
if(textureSize == Vector2f::Zero())
|
if (textureSize == Vector2f::Zero())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// SVG rasterization is determined by height (see SVGResource.cpp), and rasterization is done in terms of pixels
|
// SVG rasterization is determined by height and rasterization is done in terms of pixels.
|
||||||
// if rounding is off enough in the rasterization step (for images with extreme aspect ratios), it can cause cutoff when the aspect ratio breaks
|
// If rounding is off enough in the rasterization step (for images with extreme aspect
|
||||||
// so, we always make sure the resultant height is an integer to make sure cutoff doesn't happen, and scale width from that
|
// ratios), it can cause cutoff when the aspect ratio breaks.
|
||||||
// (you'll see this scattered throughout the function)
|
// So we always make sure the resultant height is an integer to make sure cutoff doesn't
|
||||||
// this is probably not the best way, so if you're familiar with this problem and have a better solution, please make a pull request!
|
// happen, and scale width from that (you'll see this scattered throughout the function).
|
||||||
|
// This is probably not the best way, so if you're familiar with this problem and have a
|
||||||
|
// better solution, please make a pull request!
|
||||||
|
if (mTargetIsMax) {
|
||||||
|
mSize = textureSize;
|
||||||
|
|
||||||
if(mTargetIsMax)
|
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
||||||
{
|
|
||||||
|
|
||||||
mSize = textureSize;
|
if (resizeScale.x() < resizeScale.y()) {
|
||||||
|
mSize[0] *= resizeScale.x();
|
||||||
|
mSize[1] *= resizeScale.x();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mSize[0] *= resizeScale.y();
|
||||||
|
mSize[1] *= resizeScale.y();
|
||||||
|
}
|
||||||
|
|
||||||
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
// For SVG rasterization, always calculate width from rounded height (see comment above).
|
||||||
|
mSize[1] = Math::round(mSize[1]);
|
||||||
|
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
||||||
|
|
||||||
if(resizeScale.x() < resizeScale.y())
|
}
|
||||||
{
|
else {
|
||||||
mSize[0] *= resizeScale.x();
|
// If both components are set, we just stretch.
|
||||||
mSize[1] *= resizeScale.x();
|
// If no components are set, we don't resize at all.
|
||||||
}else{
|
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
|
||||||
mSize[0] *= resizeScale.y();
|
|
||||||
mSize[1] *= resizeScale.y();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for SVG rasterization, always calculate width from rounded height (see comment above)
|
// If only one component is set, we resize in a way that maintains aspect ratio.
|
||||||
mSize[1] = Math::round(mSize[1]);
|
// For SVG rasterization, we always calculate width from rounded height (see comment above).
|
||||||
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
if (!mTargetSize.x() && mTargetSize.y()) {
|
||||||
|
mSize[1] = Math::round(mTargetSize.y());
|
||||||
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
||||||
|
}
|
||||||
|
else if (mTargetSize.x() && !mTargetSize.y()) {
|
||||||
|
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
||||||
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}else{
|
// mSize.y() should already be rounded.
|
||||||
// if both components are set, we just stretch
|
mTexture->rasterizeAt((size_t)Math::round(mSize.x()), (size_t)Math::round(mSize.y()));
|
||||||
// if no components are set, we don't resize at all
|
|
||||||
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
|
|
||||||
|
|
||||||
// if only one component is set, we resize in a way that maintains aspect ratio
|
onSizeChanged();
|
||||||
// for SVG rasterization, we always calculate width from rounded height (see comment above)
|
|
||||||
if(!mTargetSize.x() && mTargetSize.y())
|
|
||||||
{
|
|
||||||
mSize[1] = Math::round(mTargetSize.y());
|
|
||||||
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
||||||
}else if(mTargetSize.x() && !mTargetSize.y())
|
|
||||||
{
|
|
||||||
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
|
||||||
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// mSize.y() should already be rounded
|
|
||||||
mTexture->rasterizeAt((size_t)Math::round(mSize.x()), (size_t)Math::round(mSize.y()));
|
|
||||||
|
|
||||||
onSizeChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
||||||
{
|
{
|
||||||
if (!isVisible())
|
if (!isVisible())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
VideoComponent::render(parentTrans);
|
VideoComponent::render(parentTrans);
|
||||||
Transform4x4f trans = parentTrans * getTransform();
|
Transform4x4f trans = parentTrans * getTransform();
|
||||||
GuiComponent::renderChildren(trans);
|
GuiComponent::renderChildren(trans);
|
||||||
Renderer::setMatrix(trans);
|
Renderer::setMatrix(trans);
|
||||||
|
|
||||||
if (mIsPlaying && mContext.valid)
|
if (mIsPlaying && mContext.valid) {
|
||||||
{
|
const unsigned int fadeIn = (unsigned int)(Math::clamp(0.0f, mFadeIn, 1.0f) * 255.0f);
|
||||||
const unsigned int fadeIn = (unsigned int)(Math::clamp(0.0f, mFadeIn, 1.0f) * 255.0f);
|
const unsigned int color =
|
||||||
const unsigned int color = Renderer::convertColor((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
|
Renderer::convertColor((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
|
||||||
Renderer::Vertex vertices[4];
|
Renderer::Vertex vertices[4];
|
||||||
|
|
||||||
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 };
|
||||||
|
|
||||||
// round vertices
|
// Round vertices.
|
||||||
for(int i = 0; i < 4; ++i)
|
for (int i = 0; i < 4; ++i)
|
||||||
vertices[i].pos.round();
|
vertices[i].pos.round();
|
||||||
|
|
||||||
// Build a texture for the video frame
|
// Build a texture for the video frame.
|
||||||
mTexture->initFromPixels((unsigned char*)mContext.surface->pixels, mContext.surface->w, mContext.surface->h);
|
mTexture->initFromPixels((unsigned char*)mContext.surface->pixels,
|
||||||
mTexture->bind();
|
mContext.surface->w, mContext.surface->h);
|
||||||
|
mTexture->bind();
|
||||||
|
|
||||||
// Render it
|
// Render it.
|
||||||
Renderer::drawTriangleStrips(&vertices[0], 4);
|
Renderer::drawTriangleStrips(&vertices[0], 4);
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
{
|
VideoComponent::renderSnapshot(parentTrans);
|
||||||
VideoComponent::renderSnapshot(parentTrans);
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::setupContext()
|
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, (int)mVideoWidth,
|
||||||
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
|
(int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
|
||||||
mContext.mutex = SDL_CreateMutex();
|
mContext.mutex = SDL_CreateMutex();
|
||||||
mContext.valid = true;
|
mContext.valid = true;
|
||||||
resize();
|
resize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::freeContext()
|
void VideoVlcComponent::freeContext()
|
||||||
{
|
{
|
||||||
if (mContext.valid)
|
if (mContext.valid) {
|
||||||
{
|
SDL_FreeSurface(mContext.surface);
|
||||||
SDL_FreeSurface(mContext.surface);
|
SDL_DestroyMutex(mContext.mutex);
|
||||||
SDL_DestroyMutex(mContext.mutex);
|
mContext.valid = false;
|
||||||
mContext.valid = false;
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::setupVLC(std::string subtitles)
|
void VideoVlcComponent::setupVLC(std::string subtitles)
|
||||||
{
|
{
|
||||||
// If VLC hasn't been initialised yet then do it now
|
// If VLC hasn't been initialised yet then do it now.
|
||||||
if (!mVLC)
|
if (!mVLC) {
|
||||||
{
|
const char** args;
|
||||||
const char** args;
|
const char* newargs[] = { "--quiet", "--sub-file", subtitles.c_str() };
|
||||||
const char* newargs[] = { "--quiet", "--sub-file", subtitles.c_str() };
|
const char* singleargs[] = { "--quiet" };
|
||||||
const char* singleargs[] = { "--quiet" };
|
int argslen = 0;
|
||||||
int argslen = 0;
|
|
||||||
|
|
||||||
if (!subtitles.empty())
|
if (!subtitles.empty()) {
|
||||||
{
|
argslen = sizeof(newargs) / sizeof(newargs[0]);
|
||||||
argslen = sizeof(newargs) / sizeof(newargs[0]);
|
args = newargs;
|
||||||
args = newargs;
|
}
|
||||||
}
|
else {
|
||||||
else
|
argslen = sizeof(singleargs) / sizeof(singleargs[0]);
|
||||||
{
|
args = singleargs;
|
||||||
argslen = sizeof(singleargs) / sizeof(singleargs[0]);
|
}
|
||||||
args = singleargs;
|
mVLC = libvlc_new(argslen, args);
|
||||||
}
|
}
|
||||||
mVLC = libvlc_new(argslen, args);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::handleLooping()
|
void VideoVlcComponent::handleLooping()
|
||||||
{
|
{
|
||||||
if (mIsPlaying && mMediaPlayer)
|
if (mIsPlaying && mMediaPlayer) {
|
||||||
{
|
libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer);
|
||||||
libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer);
|
if (state == libvlc_Ended) {
|
||||||
if (state == libvlc_Ended)
|
if (!Settings::getInstance()->getBool("VideoAudio") ||
|
||||||
{
|
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
|
||||||
if (!Settings::getInstance()->getBool("VideoAudio") ||
|
libvlc_audio_set_mute(mMediaPlayer, 1);
|
||||||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
|
|
||||||
{
|
//libvlc_media_player_set_position(mMediaPlayer, 0.0f);
|
||||||
libvlc_audio_set_mute(mMediaPlayer, 1);
|
libvlc_media_player_set_media(mMediaPlayer, mMedia);
|
||||||
}
|
libvlc_media_player_play(mMediaPlayer);
|
||||||
//libvlc_media_player_set_position(mMediaPlayer, 0.0f);
|
}
|
||||||
libvlc_media_player_set_media(mMediaPlayer, mMedia);
|
}
|
||||||
libvlc_media_player_play(mMediaPlayer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::startVideo()
|
void VideoVlcComponent::startVideo()
|
||||||
{
|
{
|
||||||
if (!mIsPlaying) {
|
if (!mIsPlaying) {
|
||||||
mVideoWidth = 0;
|
mVideoWidth = 0;
|
||||||
mVideoHeight = 0;
|
mVideoHeight = 0;
|
||||||
|
|
||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
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
|
mPlayingVideoPath = mVideoPath;
|
||||||
mPlayingVideoPath = mVideoPath;
|
|
||||||
|
|
||||||
// Open the media
|
// Open the media.
|
||||||
mMedia = libvlc_media_new_path(mVLC, path.c_str());
|
mMedia = libvlc_media_new_path(mVLC, path.c_str());
|
||||||
if (mMedia)
|
if (mMedia) {
|
||||||
{
|
unsigned track_count;
|
||||||
unsigned track_count;
|
int parseResult;
|
||||||
int parseResult;
|
libvlc_event_t vlcEvent;
|
||||||
libvlc_event_t vlcEvent;
|
|
||||||
|
|
||||||
// Asynchronous media parsing
|
// Asynchronous media parsing.
|
||||||
libvlc_event_attach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
|
libvlc_event_attach(libvlc_media_event_manager(
|
||||||
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
|
mMedia), libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
|
||||||
|
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
|
||||||
|
|
||||||
if (!parseResult)
|
if (!parseResult) {
|
||||||
{
|
// Wait for a maximum of 1 second for the media parsing.
|
||||||
// Wait for a maximum of 1 second for the media parsing
|
for (int i = 0; i < 200; i++) {
|
||||||
for(int i=0; i<200; i++)
|
if (libvlc_media_get_parsed_status(mMedia))
|
||||||
{
|
break;
|
||||||
if ((libvlc_media_get_parsed_status(mMedia)))
|
SDL_Delay(5);
|
||||||
break;
|
};
|
||||||
SDL_Delay(5);
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
libvlc_media_track_t** tracks;
|
libvlc_media_track_t** tracks;
|
||||||
track_count = libvlc_media_tracks_get(mMedia, &tracks);
|
track_count = libvlc_media_tracks_get(mMedia, &tracks);
|
||||||
for (unsigned track = 0; track < track_count; ++track)
|
for (unsigned track = 0; track < track_count; ++track) {
|
||||||
{
|
if (tracks[track]->i_type == libvlc_track_video) {
|
||||||
if (tracks[track]->i_type == libvlc_track_video)
|
mVideoWidth = tracks[track]->video->i_width;
|
||||||
{
|
mVideoHeight = tracks[track]->video->i_height;
|
||||||
mVideoWidth = tracks[track]->video->i_width;
|
break;
|
||||||
mVideoHeight = tracks[track]->video->i_height;
|
}
|
||||||
break;
|
}
|
||||||
}
|
libvlc_media_tracks_release(tracks, track_count);
|
||||||
}
|
|
||||||
libvlc_media_tracks_release(tracks, track_count);
|
|
||||||
|
|
||||||
// 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)) {
|
||||||
{
|
#ifndef _RPI_
|
||||||
#ifndef _RPI_
|
if (mScreensaverMode) {
|
||||||
if (mScreensaverMode)
|
if (!Settings::getInstance()->getBool("CaptionsCompatibility")) {
|
||||||
{
|
|
||||||
if(!Settings::getInstance()->getBool("CaptionsCompatibility")) {
|
|
||||||
|
|
||||||
Vector2f resizeScale((Renderer::getScreenWidth() / (float)mVideoWidth), (Renderer::getScreenHeight() / (float)mVideoHeight));
|
Vector2f resizeScale((Renderer::getScreenWidth() / (float)mVideoWidth),
|
||||||
|
(Renderer::getScreenHeight() / (float)mVideoHeight));
|
||||||
|
|
||||||
if(resizeScale.x() < resizeScale.y())
|
if (resizeScale.x() < resizeScale.y()) {
|
||||||
{
|
mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.x());
|
||||||
mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.x());
|
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.x());
|
||||||
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.x());
|
}
|
||||||
}else{
|
else {
|
||||||
mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.y());
|
mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.y());
|
||||||
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.y());
|
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.y());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
PowerSaver::pause();
|
PowerSaver::pause();
|
||||||
setupContext();
|
setupContext();
|
||||||
|
|
||||||
// Setup the media player
|
// Setup the media player.
|
||||||
mMediaPlayer = libvlc_media_player_new_from_media(mMedia);
|
mMediaPlayer = libvlc_media_player_new_from_media(mMedia);
|
||||||
|
|
||||||
if (!Settings::getInstance()->getBool("VideoAudio") ||
|
if (!Settings::getInstance()->getBool("VideoAudio") ||
|
||||||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
|
(Settings::getInstance()->getBool("ScreenSaverVideoMute") &&
|
||||||
{
|
mScreensaverMode))
|
||||||
libvlc_audio_set_mute(mMediaPlayer, 1);
|
libvlc_audio_set_mute(mMediaPlayer, 1);
|
||||||
}
|
|
||||||
|
|
||||||
libvlc_media_player_play(mMediaPlayer);
|
libvlc_media_player_play(mMediaPlayer);
|
||||||
libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display, (void*)&mContext);
|
libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display,
|
||||||
libvlc_video_set_format(mMediaPlayer, "RGBA", (int)mVideoWidth, (int)mVideoHeight, (int)mVideoWidth * 4);
|
(void*)&mContext);
|
||||||
|
libvlc_video_set_format(mMediaPlayer, "RGBA", (int)mVideoWidth,
|
||||||
|
(int)mVideoHeight, (int)mVideoWidth * 4);
|
||||||
|
|
||||||
// Update the playing state
|
// Update the playing state.
|
||||||
mIsPlaying = true;
|
mIsPlaying = true;
|
||||||
mFadeIn = 0.0f;
|
mFadeIn = 0.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoVlcComponent::stopVideo()
|
void VideoVlcComponent::stopVideo()
|
||||||
{
|
{
|
||||||
mIsPlaying = false;
|
mIsPlaying = false;
|
||||||
mStartDelayed = false;
|
mStartDelayed = false;
|
||||||
// Release the media player so it stops calling back to us
|
// Release the media player so it stops calling back to us.
|
||||||
if (mMediaPlayer)
|
if (mMediaPlayer) {
|
||||||
{
|
libvlc_media_player_stop(mMediaPlayer);
|
||||||
libvlc_media_player_stop(mMediaPlayer);
|
libvlc_media_player_release(mMediaPlayer);
|
||||||
libvlc_media_player_release(mMediaPlayer);
|
libvlc_media_release(mMedia);
|
||||||
libvlc_media_release(mMedia);
|
mMediaPlayer = nullptr;
|
||||||
mMediaPlayer = NULL;
|
freeContext();
|
||||||
freeContext();
|
PowerSaver::resume();
|
||||||
PowerSaver::resume();
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
|
//
|
||||||
|
// VideoVlcComponent.h
|
||||||
|
//
|
||||||
|
// Video playing using libVLC.
|
||||||
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#ifndef ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
#ifndef ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
||||||
#define ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
#define ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
||||||
|
|
||||||
#include "VideoComponent.h"
|
#include "VideoComponent.h"
|
||||||
|
|
||||||
#include <vlc/vlc.h>
|
#include <vlc/vlc.h>
|
||||||
|
|
||||||
struct SDL_mutex;
|
struct SDL_mutex;
|
||||||
|
@ -12,64 +19,62 @@ struct libvlc_media_t;
|
||||||
struct libvlc_media_player_t;
|
struct libvlc_media_player_t;
|
||||||
|
|
||||||
struct VideoContext {
|
struct VideoContext {
|
||||||
SDL_Surface* surface;
|
SDL_Surface* surface;
|
||||||
SDL_mutex* mutex;
|
SDL_mutex* mutex;
|
||||||
bool valid;
|
bool valid;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VideoVlcComponent : public VideoComponent
|
class VideoVlcComponent : public VideoComponent
|
||||||
{
|
{
|
||||||
// Structure that groups together the configuration of the video component
|
// Structure that groups together the configuration of the video component.
|
||||||
struct Configuration
|
struct Configuration {
|
||||||
{
|
unsigned startDelay;
|
||||||
unsigned startDelay;
|
bool showSnapshotNoVideo;
|
||||||
bool showSnapshotNoVideo;
|
bool showSnapshotDelay;
|
||||||
bool showSnapshotDelay;
|
std::string defaultVideoPath;
|
||||||
std::string defaultVideoPath;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static void setupVLC(std::string subtitles);
|
static void setupVLC(std::string subtitles);
|
||||||
|
|
||||||
VideoVlcComponent(Window* window, std::string subtitles);
|
VideoVlcComponent(Window* window, std::string subtitles);
|
||||||
virtual ~VideoVlcComponent();
|
virtual ~VideoVlcComponent();
|
||||||
|
|
||||||
void render(const Transform4x4f& parentTrans) override;
|
void render(const Transform4x4f& parentTrans) override;
|
||||||
|
|
||||||
|
// Resize the video 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
|
||||||
|
// zero, no resizing. This can be set before or after a video is loaded.
|
||||||
|
// setMaxSize() and setResize() are mutually exclusive.
|
||||||
|
void setResize(float width, float height) override;
|
||||||
|
|
||||||
// Resize the video to fit this size. If one axis is zero, scale that axis to maintain aspect ratio.
|
// Resize the video to be as large as possible but fit within a box of this size.
|
||||||
// If both are non-zero, potentially break the aspect ratio. If both are zero, no resizing.
|
// This can be set before or after a video is loaded.
|
||||||
// Can be set before or after a video is loaded.
|
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
||||||
// setMaxSize() and setResize() are mutually exclusive.
|
void setMaxSize(float width, float height) override;
|
||||||
void setResize(float width, float height) override;
|
|
||||||
|
|
||||||
// Resize the video to be as large as possible but fit within a box of this size.
|
|
||||||
// Can be set before or after a video is loaded.
|
|
||||||
// Never breaks the aspect ratio. setMaxSize() and setResize() are mutually exclusive.
|
|
||||||
void setMaxSize(float width, float height) override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
|
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
|
||||||
// Used internally whenever the resizing parameters or texture change.
|
// Used internally whenever the resizing parameters or texture change.
|
||||||
void resize();
|
void resize();
|
||||||
// Start the video Immediately
|
// Start the video immediately.
|
||||||
virtual void startVideo() override;
|
virtual void startVideo() override;
|
||||||
// Stop the video
|
// Stop the video.
|
||||||
virtual void stopVideo() override;
|
virtual void stopVideo() override;
|
||||||
// Handle looping the video. Must be called periodically
|
// Handle looping the video. Must be called periodically.
|
||||||
virtual void handleLooping() override;
|
virtual void handleLooping() override;
|
||||||
|
|
||||||
void setupContext();
|
void setupContext();
|
||||||
void freeContext();
|
void freeContext();
|
||||||
|
|
||||||
static void VlcMediaParseCallback(const libvlc_event_t *event, void *user_data) {};
|
static void VlcMediaParseCallback(const libvlc_event_t *event, void *user_data) {};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static libvlc_instance_t* mVLC;
|
static libvlc_instance_t* mVLC;
|
||||||
libvlc_media_t* mMedia;
|
libvlc_media_t* mMedia;
|
||||||
libvlc_media_player_t* mMediaPlayer;
|
libvlc_media_player_t* mMediaPlayer;
|
||||||
VideoContext mContext;
|
VideoContext mContext;
|
||||||
std::shared_ptr<TextureResource> mTexture;
|
std::shared_ptr<TextureResource> mTexture;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
#endif // ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
|
||||||
|
|
|
@ -211,29 +211,29 @@ GuiInputConfig::GuiInputConfig(
|
||||||
Input input;
|
Input input;
|
||||||
okFunction();
|
okFunction();
|
||||||
// Temporarily commented out, needs to be properly cleaned up later.
|
// Temporarily commented out, needs to be properly cleaned up later.
|
||||||
// if (!mTargetConfig->getInputByName("HotKeyEnable", &input)) {
|
// if (!mTargetConfig->getInputByName("HotKeyEnable", &input)) {
|
||||||
// mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
// mWindow->pushGui(new GuiMsgBox(mWindow, getHelpStyle(),
|
||||||
// "YOU DIDN'T CHOOSE A HOTKEY ENABLE BUTTON. THIS IS REQUIRED FOR EXITING GAMES "
|
// "YOU DIDN'T CHOOSE A HOTKEY ENABLE BUTTON. THIS IS REQUIRED FOR EXITING GAMES "
|
||||||
// "WITH A CONTROLLER. DO YOU WANT TO USE THE SELECT BUTTON DEFAULT ? PLEASE ANSWER "
|
// "WITH A CONTROLLER. DO YOU WANT TO USE THE SELECT BUTTON DEFAULT ? PLEASE ANSWER "
|
||||||
// "YES TO USE SELECT OR NO TO NOT SET A HOTKEY ENABLE BUTTON.",
|
// "YES TO USE SELECT OR NO TO NOT SET A HOTKEY ENABLE BUTTON.",
|
||||||
// "YES", [this, okFunction] {
|
// "YES", [this, okFunction] {
|
||||||
// Input input;
|
// Input input;
|
||||||
// mTargetConfig->getInputByName("Select", &input);
|
// mTargetConfig->getInputByName("Select", &input);
|
||||||
// mTargetConfig->mapInput("HotKeyEnable", input);
|
// mTargetConfig->mapInput("HotKeyEnable", input);
|
||||||
// okFunction();
|
// okFunction();
|
||||||
// },
|
// },
|
||||||
// "NO", [this, okFunction] {
|
// "NO", [this, okFunction] {
|
||||||
// // for a disabled hotkey enable button, set to a key with id 0,
|
// // for a disabled hotkey enable button, set to a key with id 0,
|
||||||
// // so the input configuration script can be backwards compatible.
|
// // so the input configuration script can be backwards compatible.
|
||||||
// mTargetConfig->mapInput("HotKeyEnable", Input(DEVICE_KEYBOARD,
|
// mTargetConfig->mapInput("HotKeyEnable", Input(DEVICE_KEYBOARD,
|
||||||
// TYPE_KEY, 0, 1, true));
|
// TYPE_KEY, 0, 1, true));
|
||||||
// okFunction();
|
// okFunction();
|
||||||
// }
|
// }
|
||||||
// ));
|
// ));
|
||||||
// }
|
// }
|
||||||
// else {
|
// else {
|
||||||
// okFunction();
|
// okFunction();
|
||||||
// }
|
// }
|
||||||
}));
|
}));
|
||||||
|
|
||||||
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
mButtonGrid = makeButtonGrid(mWindow, buttons);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// Renderer.cpp
|
// Renderer.cpp
|
||||||
//
|
//
|
||||||
// Rendering functions.
|
// General rendering functions.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "renderers/Renderer.h"
|
#include "renderers/Renderer.h"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
//
|
//
|
||||||
// Renderer.h
|
// Renderer.h
|
||||||
//
|
//
|
||||||
// Rendering functions.
|
// General rendering functions.
|
||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
|
@ -60,7 +60,7 @@ const ResourceData ResourceManager::getFileData(const std::string& path) const
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file doesn't exist, return an "empty" ResourceData.
|
// If the file doesn't exist, return an "empty" ResourceData.
|
||||||
ResourceData data = {NULL, 0};
|
ResourceData data = {nullptr, 0};
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ bool TextureData::initSVGFromMemory(const unsigned char* fileData, size_t length
|
||||||
|
|
||||||
// nsvgParse excepts a modifiable, null-terminated string.
|
// nsvgParse excepts a modifiable, null-terminated string.
|
||||||
char* copy = (char*)malloc(length + 1);
|
char* copy = (char*)malloc(length + 1);
|
||||||
assert(copy != NULL);
|
assert(copy != nullptr);
|
||||||
memcpy(copy, fileData, length);
|
memcpy(copy, fileData, length);
|
||||||
copy[length] = '\0';
|
copy[length] = '\0';
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue