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:
Leon Styhre 2020-06-28 18:39:18 +02:00
parent 5b17edde8b
commit e4fdd1e20d
53 changed files with 3831 additions and 3642 deletions

View file

@ -25,7 +25,7 @@ Some key points:
* 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()
* 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
* For the rest, check the code and have fun! :)

View file

@ -59,7 +59,7 @@ GuiVideoScreensaverOptions::GuiVideoScreensaverOptions(Window* window, const cha
// Set subtitle position.
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;
align_mode.push_back("left");
align_mode.push_back("center");
@ -132,7 +132,7 @@ void GuiVideoScreensaverOptions::save()
"never" && Settings::getInstance()->getBool("ScreenSaverOmxPlayer"));
if (startingStatusNotRisky && endStatusRisky) {
// 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 "
"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 > "

View file

@ -44,11 +44,11 @@ bool HttpReq::isUrl(const std::string& str)
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();
if (mHandle == NULL) {
if (mHandle == nullptr) {
mStatus = REQ_IO_ERROR;
onError("curl_easy_init failed");
return;
@ -147,7 +147,7 @@ HttpReq::Status HttpReq::status()
if (msg->msg == CURLMSG_DONE) {
HttpReq* req = s_requests[msg->easy_handle];
if (req == NULL) {
if (req == nullptr) {
LOG(LogError) << "Cannot find easy handle!";
continue;
}

View file

@ -69,7 +69,7 @@ void touch(const std::string& filename)
{
#ifdef WIN32
FILE* fp = fopen(filename.c_str(), "ab+");
if (fp != NULL)
if (fp != nullptr)
fclose(fp);
#else
int fd = open(filename.c_str(), O_CREAT|O_WRONLY, 0644);

View file

@ -11,11 +11,12 @@
#include "Log.h"
#include "Scripting.h"
#include "Platform.h"
#include <pugixml.hpp>
#include <algorithm>
#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
// the in-program settings menu. Most can be set using command-line arguments,
@ -59,7 +60,7 @@ Settings::Settings()
Settings* Settings::getInstance()
{
if (sInstance == NULL)
if (sInstance == nullptr)
sInstance = new Settings();
return sInstance;

View file

@ -82,7 +82,7 @@ bool NavigationSounds::isPlayingThemeNavigationSound(NavigationSoundsID soundID)
Sound::Sound(
const std::string & path)
: mSampleData(NULL),
: mSampleData(nullptr),
mSamplePos(0),
mSampleLength(0),
playing(false)
@ -103,7 +103,7 @@ void Sound::loadFile(const std::string & path)
void Sound::init()
{
if(mSampleData != NULL)
if(mSampleData != nullptr)
deinit();
if(mPath.empty())

View file

@ -1,10 +1,17 @@
//
// AnimatedImageComponent.cpp
//
// Creates animation from multiple images files.
//
#include "components/AnimatedImageComponent.h"
#include "components/ImageComponent.h"
#include "resources/ResourceManager.h"
#include "Log.h"
AnimatedImageComponent::AnimatedImageComponent(Window* window) : GuiComponent(window), mEnabled(false)
AnimatedImageComponent::AnimatedImageComponent(Window* window)
: GuiComponent(window), mEnabled(false)
{
}
@ -14,11 +21,11 @@ void AnimatedImageComponent::load(const AnimationDef* def)
assert(def->frameCount >= 1);
for(size_t i = 0; i < def->frameCount; i++)
{
if(def->frames[i].path != NULL && !ResourceManager::getInstance()->fileExists(def->frames[i].path))
{
LOG(LogError) << "Missing animation frame " << i << " (\"" << def->frames[i].path << "\")";
for (size_t i = 0; i < def->frameCount; i++) {
if (def->frames[i].path != nullptr &&
!ResourceManager::getInstance()->fileExists(def->frames[i].path)) {
LOG(LogError) << "Missing animation frame " << i <<
" (\"" << def->frames[i].path << "\")";
continue;
}
@ -44,8 +51,7 @@ void AnimatedImageComponent::reset()
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());
}
}
@ -57,18 +63,16 @@ void AnimatedImageComponent::update(int deltaTime)
mFrameAccumulator += deltaTime;
while(mFrames.at(mCurrentFrame).second <= mFrameAccumulator)
{
while (mFrames.at(mCurrentFrame).second <= mFrameAccumulator) {
mCurrentFrame++;
if(mCurrentFrame == (int)mFrames.size())
{
if(mLoop)
{
// restart
if (mCurrentFrame == (int)mFrames.size()) {
if (mLoop) {
// Restart.
mCurrentFrame = 0;
}else{
// done, stop at last frame
}
else {
// Done, stop at last frame.
mCurrentFrame--;
mEnabled = false;
break;

View file

@ -1,3 +1,9 @@
//
// AnimatedImageComponent.h
//
// Creates animation from multiple images files.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
#define ES_CORE_COMPONENTS_ANIMATED_IMAGE_COMPONENT_H
@ -6,14 +12,12 @@
class ImageComponent;
struct AnimationFrame
{
struct AnimationFrame {
const char* path;
int time;
};
struct AnimationDef
{
struct AnimationDef {
AnimationFrame* frames;
size_t frameCount;
bool loop;
@ -24,9 +28,9 @@ class AnimatedImageComponent : public GuiComponent
public:
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 render(const Transform4x4f& trans) override;

View file

@ -1,16 +1,23 @@
//
// BusyComponent.cpp
//
// Animated busy indicator.
//
#include "BusyComponent.h"
#include "components/AnimatedImageComponent.h"
#include "components/ImageComponent.h"
#include "components/TextComponent.h"
// animation definition
// Animation definition.
AnimationFrame BUSY_ANIMATION_FRAMES[] = {
{":/graphics/busy_0.svg", 300},
{":/graphics/busy_1.svg", 300},
{":/graphics/busy_2.svg", 300},
{":/graphics/busy_3.svg", 300},
};
const AnimationDef BUSY_ANIMATION_DEF = { BUSY_ANIMATION_FRAMES, 4, true };
BusyComponent::BusyComponent(Window* window): GuiComponent(window),
@ -18,9 +25,10 @@ BusyComponent::BusyComponent(Window* window) : GuiComponent(window),
{
mAnimation = std::make_shared<AnimatedImageComponent>(mWindow);
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(mText, Vector2i(3, 1), false, true);
@ -40,13 +48,14 @@ void BusyComponent::onSizeChanged()
mText->setSize(0, textHeight);
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(3, textWidth / mSize.x());
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) +
mGrid.getColWidth(2) + mGrid.getColWidth(3), textHeight + 2),
mAnimation->getPosition(), Vector2f(0, 0));
}

View file

@ -1,3 +1,9 @@
//
// BusyComponent.h
//
// Animated busy indicator.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_BUSY_COMPONENT_H
#define ES_CORE_COMPONENTS_BUSY_COMPONENT_H
@ -16,7 +22,7 @@ public:
void onSizeChanged() override;
void reset(); // reset to frame 0
void reset(); // Reset to frame 0.
private:
NinePatchComponent mBackground;

View file

@ -1,9 +1,19 @@
//
// ButtonComponent.cpp
//
// Basic on/off button.
//
#include "components/ButtonComponent.h"
#include "resources/Font.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(
Window* window, const std::string& text,
const std::string& helpText,
const std::function<void()>& func)
: GuiComponent(window),
mBox(window, ":/graphics/button.png"),
mFont(Font::get(FONT_SIZE_MEDIUM)),
mFocused(false),
@ -27,8 +37,7 @@ void ButtonComponent::setPressedFunc(std::function<void()> f)
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)
mPressedFunc();
return true;
@ -70,8 +79,7 @@ void ButtonComponent::setEnabled(bool enabled)
void ButtonComponent::updateImage()
{
if(!mEnabled || !mPressedFunc)
{
if (!mEnabled || !mPressedFunc) {
mBox.setImagePath(":/graphics/button_filled.png");
mBox.setCenterColor(0x770000FF);
mBox.setEdgeColor(0x770000FF);
@ -91,7 +99,8 @@ void ButtonComponent::render(const Transform4x4f& parentTrans)
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,
(mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
trans = trans.translate(centerOffset);
Renderer::setMatrix(trans);

View file

@ -1,3 +1,9 @@
//
// ButtonComponent.h
//
// Basic on/off button.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
#define ES_CORE_COMPONENTS_BUTTON_COMPONENT_H
@ -10,10 +16,10 @@ class TextCache;
class ButtonComponent : public GuiComponent
{
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 setEnabled(bool enable);
bool input(InputConfig* config, Input input) override;

View file

@ -100,7 +100,7 @@ void ComponentGrid::setEntry(
{
assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
assert(comp != nullptr);
assert(comp->getParent() == NULL);
assert(comp->getParent() == nullptr);
GridEntry entry(pos, size, comp, canFocus, resize, updateType, border);
mCells.push_back(entry);
@ -223,7 +223,7 @@ const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
return &(*it);
}
return NULL;
return nullptr;
}
bool ComponentGrid::input(InputConfig* config, Input input)
@ -327,7 +327,7 @@ void ComponentGrid::onFocusGained()
bool ComponentGrid::cursorValid()
{
const GridEntry* e = getCellAt(mCursor);
return (e != NULL && e->canFocus);
return (e != nullptr && e->canFocus);
}
void ComponentGrid::update(int deltaTime)
@ -359,7 +359,7 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
void ComponentGrid::textInput(const char* text)
{
const GridEntry* selectedEntry = getCellAt(mCursor);
if (selectedEntry != NULL && selectedEntry->canFocus)
if (selectedEntry != nullptr && selectedEntry->canFocus)
selectedEntry->component->textInput(text);
}

View file

@ -104,7 +104,7 @@ private:
operator bool() const
{
return component != NULL;
return component != nullptr;
}
};

View file

@ -20,7 +20,7 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
{
IList<ComponentListRow, void*>::Entry e;
e.name = "";
e.object = NULL;
e.object = nullptr;
e.data = row;
this->add(e);

View file

@ -1,3 +1,9 @@
//
// GridTileComponent.cpp
//
// X*Y grid.
//
#include "GridTileComponent.h"
#include "animations/LambdaAnimation.h"
@ -44,7 +50,7 @@ void GridTileComponent::render(const Transform4x4f& parentTrans)
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)
{
GuiComponent::update(deltaTime);
@ -62,7 +68,8 @@ void GridTileComponent::update(int deltaTime)
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"))
properties->mSize = elem->get<Vector2f>("size") * screen;
@ -79,8 +86,7 @@ void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTilePropert
if (elem->has("backgroundCornerSize"))
properties->mBackgroundCornerSize = elem->get<Vector2f>("backgroundCornerSize");
if (elem->has("backgroundColor"))
{
if (elem->has("backgroundColor")) {
properties->mBackgroundCenterColor = elem->get<unsigned int>("backgroundColor");
properties->mBackgroundEdgeColor = elem->get<unsigned int>("backgroundColor");
}
@ -92,18 +98,20 @@ void applyThemeToProperties(const ThemeData::ThemeElement* elem, GridTilePropert
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");
if (elem)
applyThemeToProperties(elem, &mDefaultProperties);
// Apply theme to the selected gridtile
// NOTE that some of the default gridtile properties influence on the selected gridtile properties
// See THEMES.md for more informations
// Apply theme to the selected gridtile. Note that some of the default gridtile
// properties have influence on the selected gridtile properties.
// See THEMES.md for more informations.
elem = theme->getElement(view, "selected", "gridtile");
mSelectedProperties.mSize = getSelectedTileSize();
@ -115,11 +123,12 @@ void GridTileComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, cons
applyThemeToProperties(elem, &mSelectedProperties);
}
// Made this a static function because the ImageGridComponent need to know the default tile size
// to calculate the grid dimension before it instantiate the GridTileComponents
// Made this a static function because the ImageGridComponent needs to know the default tile
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
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;
}
@ -143,7 +152,7 @@ void GridTileComponent::setImage(const std::string& path)
{
mImage->setImage(path);
// Resize now to prevent flickering images when scrolling
// Resize now to prevent flickering images when scrolling.
resize();
}
@ -151,23 +160,20 @@ void GridTileComponent::setImage(const std::shared_ptr<TextureResource>& texture
{
mImage->setImage(texture);
// Resize now to prevent flickering images when scrolling
// Resize now to prevent flickering images when scrolling.
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)
{
return;
}
mSelected = selected;
if (selected)
{
if (pPosition == NULL || !allowAnimation)
{
if (selected) {
if (pPosition == nullptr || !allowAnimation) {
cancelAnimation(3);
this->setSelectedZoom(1);
@ -175,15 +181,12 @@ void GridTileComponent::setSelected(bool selected, bool allowAnimation, Vector3f
resize();
}
else
{
else {
mAnimPosition = Vector3f(pPosition->x(), pPosition->y(), pPosition->z());
auto func = [this](float t)
{
t -= 1; // cubic ease out
auto func = [this](float t) {
t -= 1; // Cubic ease out.
float pct = Math::lerp(0, 1, t*t*t + 1);
this->setSelectedZoom(pct);
};
@ -194,22 +197,19 @@ void GridTileComponent::setSelected(bool selected, bool allowAnimation, Vector3f
}, false, 3);
}
}
else // if (!selected)
{
if (!allowAnimation)
{
// If (!selected).
else {
if (!allowAnimation) {
cancelAnimation(3);
this->setSelectedZoom(0);
resize();
}
else
{
else {
this->setSelectedZoom(1);
auto func = [this](float t)
{
t -= 1; // cubic ease out
auto func = [this](float t) {
t -= 1; // Cubic ease out.
float pct = Math::lerp(0, 1, t*t*t + 1);
this->setSelectedZoom(1.0 - pct);
};
@ -272,34 +272,32 @@ void GridTileComponent::calcCurrentProperties()
float zoomPercentInverse = 1.0 - mSelectedZoomPercent;
if (mSelectedZoomPercent != 0.0f && mSelectedZoomPercent != 1.0f) {
if (mDefaultProperties.mSize != mSelectedProperties.mSize) {
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse + mSelectedProperties.mSize * mSelectedZoomPercent;
}
if (mDefaultProperties.mSize != mSelectedProperties.mSize)
mCurrentProperties.mSize = mDefaultProperties.mSize * zoomPercentInverse +
mSelectedProperties.mSize * mSelectedZoomPercent;
if (mDefaultProperties.mPadding != mSelectedProperties.mPadding)
{
mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse + mSelectedProperties.mPadding * mSelectedZoomPercent;
}
mCurrentProperties.mPadding = mDefaultProperties.mPadding * zoomPercentInverse +
mSelectedProperties.mPadding * mSelectedZoomPercent;
if (mDefaultProperties.mImageColor != mSelectedProperties.mImageColor)
{
mCurrentProperties.mImageColor = mixColors(mDefaultProperties.mImageColor, mSelectedProperties.mImageColor, mSelectedZoomPercent);
}
mCurrentProperties.mImageColor = mixColors(mDefaultProperties.mImageColor,
mSelectedProperties.mImageColor, mSelectedZoomPercent);
if (mDefaultProperties.mBackgroundCornerSize != mSelectedProperties.mBackgroundCornerSize)
{
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize * zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize * mSelectedZoomPercent;
}
mCurrentProperties.mBackgroundCornerSize = mDefaultProperties.mBackgroundCornerSize *
zoomPercentInverse + mSelectedProperties.mBackgroundCornerSize *
mSelectedZoomPercent;
if (mDefaultProperties.mBackgroundCenterColor != mSelectedProperties.mBackgroundCenterColor)
{
mCurrentProperties.mBackgroundCenterColor = mixColors(mDefaultProperties.mBackgroundCenterColor, mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
}
mCurrentProperties.mBackgroundCenterColor =
mixColors(mDefaultProperties.mBackgroundCenterColor,
mSelectedProperties.mBackgroundCenterColor, mSelectedZoomPercent);
if (mDefaultProperties.mBackgroundEdgeColor != mSelectedProperties.mBackgroundEdgeColor)
{
mCurrentProperties.mBackgroundEdgeColor = mixColors(mDefaultProperties.mBackgroundEdgeColor, mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
}
mCurrentProperties.mBackgroundEdgeColor =
mixColors(mDefaultProperties.mBackgroundEdgeColor,
mSelectedProperties.mBackgroundEdgeColor, mSelectedZoomPercent);
}
}

View file

@ -1,3 +1,9 @@
//
// GridTileComponent.h
//
// X*Y grid.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
#define ES_CORE_COMPONENTS_GRID_TILE_COMPONENT_H
@ -5,8 +11,7 @@
#include "NinePatchComponent.h"
#include "ImageComponent.h"
struct GridTileProperties
{
struct GridTileProperties {
Vector2f mSize;
Vector2f mPadding;
unsigned int mImageColor;
@ -22,10 +27,11 @@ public:
GridTileComponent(Window* window);
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
// to calculate the grid dimension before it instantiate the GridTileComponents
// Made this a static function because the ImageGridComponent needs to know the default tile
// max size to calculate the grid dimension before it instantiates the GridTileComponents.
static Vector2f getDefaultTileSize();
Vector2f getSelectedTileSize() const;
bool isSelected() const;
@ -34,7 +40,8 @@ public:
void setImage(const std::string& path);
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,
Vector3f* pPosition = nullptr, bool force=false);
void setVisible(bool visible);
void forceSize(Vector2f size, float selectedZoom);

View file

@ -1,3 +1,9 @@
//
// HelpComponent.cpp
//
// Help information in icon and text pairs.
//
#include "components/HelpComponent.h"
#include "components/ComponentGrid.h"
@ -8,11 +14,11 @@
#include "Log.h"
#include "Settings.h"
#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_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 ICON_TEXT_SPACING 8 // space between [icon] and [text] (px)
#define ENTRY_SPACING 16 // space between [text] and next [icon] (px)
#define ICON_TEXT_SPACING 8 // Space between [icon] and [text] (px).
#define ENTRY_SPACING 16 // Space between [text] and next [icon] (px).
static const std::map<std::string, const char*> ICON_PATH_MAP {
{ "up/down", ":/help/dpad_updown.svg" },
@ -53,8 +59,7 @@ void HelpComponent::setStyle(const HelpStyle& style)
void HelpComponent::updateGrid()
{
if(!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty())
{
if (!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty()) {
mGrid.reset();
return;
}
@ -62,6 +67,7 @@ void HelpComponent::updateGrid()
std::shared_ptr<Font>& font = mStyle.font;
mGrid = std::make_shared<ComponentGrid>(mWindow, Vector2i((int)mPrompts.size() * 4, 1));
// [icon] [spacer1] [text] [spacer2]
std::vector< std::shared_ptr<ImageComponent> > icons;
@ -69,23 +75,24 @@ void HelpComponent::updateGrid()
float width = 0;
const float height = Math::round(font->getLetterHeight() * 1.25f);
for(auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++)
{
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);
auto lbl = std::make_shared<TextComponent>(mWindow,
Utils::String::toUpper(it->second), font, mStyle.textColor);
labels.push_back(lbl);
width += icon->getSize().x() + lbl->getSize().x() + ICON_TEXT_SPACING + ENTRY_SPACING;
}
mGrid->setSize(width, height);
for(unsigned int i = 0; i < icons.size(); i++)
{
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);
@ -107,14 +114,13 @@ std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
return it->second;
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 << "\"!";
return nullptr;
}
if(!ResourceManager::getInstance()->fileExists(pathLookup->second))
{
LOG(LogError) << "Help icon \"" << name << "\" - corresponding image file \"" << pathLookup->second << "\" misisng!";
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
LOG(LogError) << "Help icon \"" << name <<
"\" - corresponding image file \"" << pathLookup->second << "\" misisng!";
return nullptr;
}
@ -128,10 +134,8 @@ void HelpComponent::setOpacity(unsigned char opacity)
GuiComponent::setOpacity(opacity);
for (unsigned int i = 0; i < mGrid->getChildCount(); i++)
{
mGrid->getChild(i)->setOpacity(opacity);
}
}
void HelpComponent::render(const Transform4x4f& parentTrans)
{

View file

@ -1,3 +1,9 @@
//
// HelpComponent.h
//
// Help information in icon and text pairs.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_HELP_COMPONENT_H
#define ES_CORE_COMPONENTS_HELP_COMPONENT_H

View file

@ -1,3 +1,9 @@
//
// IList.h
//
// Gamelist base class.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_ILIST_H
#define ES_CORE_COMPONENTS_ILIST_H
@ -6,52 +12,51 @@
#include "resources/Font.h"
#include "PowerSaver.h"
enum CursorState
{
enum CursorState {
CURSOR_STOPPED,
CURSOR_SCROLLING
};
enum ListLoopType
{
enum ListLoopType {
LIST_ALWAYS_LOOP,
LIST_PAUSE_AT_END,
LIST_NEVER_LOOP
};
struct ScrollTier
{
int length; // how long we stay on this level before going to the next
int scrollDelay; // how long between scrolls
struct ScrollTier {
int length; // How long we stay on this tier before going to the next.
int scrollDelay; // How long between scrolls.
};
struct ScrollTierList
{
struct ScrollTierList {
const int count;
const ScrollTier* tiers;
};
// default scroll tiers
// Default scroll tiers.
const ScrollTier QUICK_SCROLL_TIERS[] = {
{500, 500},
{2000, 114},
{4000, 32},
{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[] = {
{500, 500},
{0, 200}
};
const ScrollTierList LIST_SCROLL_STYLE_SLOW = { 2, SLOW_SCROLL_TIERS };
template <typename EntryData, typename UserData>
class IList : public GuiComponent
{
public:
struct Entry
{
struct Entry {
std::string name;
UserData object;
EntryData data;
@ -77,8 +82,14 @@ protected:
std::vector<Entry> mEntries;
public:
IList(Window* window, const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK, const ListLoopType& loopType = LIST_PAUSE_AT_END) : GuiComponent(window),
mGradient(window), mTierList(tierList), mLoopType(loopType)
IList(
Window* window,
const ScrollTierList& tierList = LIST_SCROLL_STYLE_QUICK,
const ListLoopType& loopType = LIST_PAUSE_AT_END)
: GuiComponent(window),
mGradient(window),
mTierList(tierList),
mLoopType(loopType)
{
mCursor = 0;
mScrollTier = 0;
@ -148,13 +159,11 @@ public:
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)
{
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
{
if((*it).object == obj)
{
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
if ((*it).object == obj) {
mCursor = (int)(it - mEntries.cbegin());
onCursorChanged(CURSOR_STOPPED);
return true;
@ -164,7 +173,7 @@ public:
return false;
}
// entry management
// Entry management.
void add(const Entry& e)
{
mEntries.push_back(e);
@ -172,10 +181,8 @@ public:
bool remove(const UserData& obj)
{
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
{
if((*it).object == obj)
{
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
if ((*it).object == obj) {
remove(it);
return true;
}
@ -189,8 +196,7 @@ public:
protected:
void remove(typename std::vector<Entry>::const_iterator& it)
{
if(mCursor > 0 && it - mEntries.cbegin() <= mCursor)
{
if (mCursor > 0 && it - mEntries.cbegin() <= mCursor) {
mCursor--;
onCursorChanged(CURSOR_STOPPED);
}
@ -214,11 +220,13 @@ protected:
return true;
}
bool listInput(int velocity) // a velocity of 0 = stop scrolling
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
// Generate an onCursorChanged event in the stopped state when the user
// lets go of the key.
if (velocity == 0 && mScrollVelocity != 0)
onCursorChanged(CURSOR_STOPPED);
@ -234,9 +242,11 @@ protected:
void listUpdate(int deltaTime)
{
// update the title overlay opacity
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
// 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)
@ -250,23 +260,23 @@ protected:
mScrollCursorAccumulator += deltaTime;
mScrollTierAccumulator += deltaTime;
// we delay scrolling until after scroll tier has updated so isScrolling() returns accurately during onCursorChanged callbacks
// we don't just do scroll tier first because it would not catch the scrollDelay == tier length case
// We delay scrolling until after scroll tier has updated so isScrolling() returns
// accurately during onCursorChanged callbacks. We don't just do scroll tier first
// because it would not catch the scrollDelay == tier length case.
int scrollCount = 0;
while(mScrollCursorAccumulator >= mTierList.tiers[mScrollTier].scrollDelay)
{
while (mScrollCursorAccumulator >= mTierList.tiers[mScrollTier].scrollDelay) {
mScrollCursorAccumulator -= mTierList.tiers[mScrollTier].scrollDelay;
scrollCount++;
}
// are we ready to go even FASTER?
while(mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >= mTierList.tiers[mScrollTier].length)
{
// Are we ready to go even FASTER?
while (mScrollTier < mTierList.count - 1 && mScrollTierAccumulator >=
mTierList.tiers[mScrollTier].length) {
mScrollTierAccumulator -= mTierList.tiers[mScrollTier].length;
mScrollTier++;
}
// actually perform the scrolling
// Actually perform the scrolling.
for (int i = 0; i < scrollCount; i++)
scroll(mScrollVelocity);
}
@ -276,8 +286,10 @@ protected:
if (size() == 0 || !mTitleOverlayFont || mTitleOverlayOpacity == 0)
return;
// we don't bother caching this because it's only two letters and will change pretty much every frame if we're scrolling
const std::string text = getSelectedName().size() >= 2 ? getSelectedName().substr(0, 2) : "??";
// We don't bother caching this because it's only two letters and will change pretty
// much every frame if we're scrolling.
const std::string text = getSelectedName().size() >= 2 ?
getSelectedName().substr(0, 2) : "??";
Vector2f off = mTitleOverlayFont->sizeText(text);
off[0] = (Renderer::getScreenWidth() - off.x()) * 0.5f;
@ -288,8 +300,10 @@ protected:
mGradient.setOpacity(mTitleOverlayOpacity);
mGradient.render(identTrans);
TextCache* cache = mTitleOverlayFont->buildTextCache(text, off.x(), off.y(), 0xFFFFFF00 | mTitleOverlayOpacity);
mTitleOverlayFont->renderTextCache(cache); // relies on mGradient's render for Renderer::setMatrix()
TextCache* cache = mTitleOverlayFont->buildTextCache(text, off.x(), off.y(),
0xFFFFFF00 | mTitleOverlayOpacity);
// Relies on mGradient's render for Renderer::setMatrix()
mTitleOverlayFont->renderTextCache(cache);
delete cache;
}
@ -301,24 +315,23 @@ protected:
int cursor = mCursor + amt;
int absAmt = amt < 0 ? -amt : amt;
// stop at the end if we've been holding down the button for a long time or
// we're scrolling faster than one item at a time (e.g. page up/down)
// otherwise, loop around
// Stop at the end if we've been holding down the button for a long time or
// 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)
{
mLoopType == LIST_NEVER_LOOP) {
if (cursor < 0) {
cursor = 0;
mScrollVelocity = 0;
mScrollTier = 0;
}else if(cursor >= size())
{
}
else if (cursor >= size()) {
cursor = size() - 1;
mScrollVelocity = 0;
mScrollTier = 0;
}
}else{
}
else {
while (cursor < 0)
cursor += size();
while (cursor >= size())

View file

@ -1,3 +1,9 @@
//
// ImageComponent.cpp
//
// Handles images: loading, resizing, cropping, color shifting etc.
//
#include "components/ImageComponent.h"
#include "resources/TextureResource.h"
@ -18,10 +24,26 @@ Vector2f ImageComponent::getSize() const
return GuiComponent::getSize() * (mBottomRightCrop - mTopLeftCrop);
}
ImageComponent::ImageComponent(Window* window, bool forceLoad, 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)
ImageComponent::ImageComponent(
Window* window,
bool forceLoad,
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();
}
@ -39,73 +61,75 @@ void ImageComponent::resize()
if (textureSize == Vector2f::Zero())
return;
if(mTexture->isTiled())
{
if (mTexture->isTiled()) {
mSize = mTargetSize;
}else{
// SVG rasterization is determined by height (see SVGResource.cpp), 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
// so, we always make sure the resultant height is an integer to make sure cutoff doesn't 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;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
if(resizeScale.x() < resizeScale.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)
// 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)
{
else {
// 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.
// So we always make sure the resultant height is an integer to make sure cutoff doesn't
// 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;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
if(resizeScale.x() > resizeScale.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;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
if (resizeScale.x() > resizeScale.y()) {
mSize[0] *= resizeScale.x();
mSize[1] *= resizeScale.x();
float cropPercent = (mSize.y() - mTargetSize.y()) / (mSize.y() * 2);
crop(0, cropPercent, 0, cropPercent);
}else{
}
else {
mSize[0] *= resizeScale.y();
mSize[1] *= resizeScale.y();
float cropPercent = (mSize.x() - mTargetSize.x()) / (mSize.x() * 2);
crop(cropPercent, 0, cropPercent, 0);
}
// 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
// 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());
}else{
// if both components are set, we just stretch
// if no components are set, we don't resize at all
}
else {
// 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())
{
// 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())
{
}
else if (mTargetSize.x() && !mTargetSize.y()) {
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
}
@ -114,7 +138,7 @@ void ImageComponent::resize()
mSize[0] = Math::round(mSize.x());
mSize[1] = Math::round(mSize.y());
// mSize.y() should already be rounded
// mSize.y() should already be rounded.
mTexture->rasterizeAt((size_t)mSize.x(), (size_t)mSize.y());
onSizeChanged();
@ -132,13 +156,13 @@ void ImageComponent::setDefaultImage(std::string path)
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))
mTexture.reset();
else
mTexture = TextureResource::get(mDefaultPath, tile, mForceLoad, mDynamic);
} else {
}
else {
mTexture = TextureResource::get(path, tile, mForceLoad, mDynamic);
}
@ -274,8 +298,8 @@ void ImageComponent::updateVertices()
if (!mTexture || !mTexture->isInitialized())
return;
// 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
// 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.
const Vector2f topLeft = { mSize * mTopLeftCrop };
const Vector2f bottomRight = { mSize * mBottomRightCrop };
const float px = mTexture->isTiled() ? mSize.x() / getTextureSize().x() : 1.0f;
@ -288,18 +312,16 @@ void ImageComponent::updateVertices()
updateColors();
// round vertices
// Round vertices.
for (int i = 0; i < 4; ++i)
mVertices[i].pos.round();
if(mFlipX)
{
if (mFlipX) {
for (int i = 0; i < 4; ++i)
mVertices[i].tex[0] = px - mVertices[i].tex[0];
}
if(mFlipY)
{
if (mFlipY) {
for (int i = 0; i < 4; ++i)
mVertices[i].tex[1] = py - mVertices[i].tex[1];
}
@ -308,8 +330,10 @@ void ImageComponent::updateVertices()
void ImageComponent::updateColors()
{
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 colorEnd = Renderer::convertColor(mColorShiftEnd & 0xFFFFFF00 | (unsigned char)((mColorShiftEnd & 0xFF) * opacity));
const unsigned int color = Renderer::convertColor(mColorShift & 0xFFFFFF00 |
(unsigned char)((mColorShift & 0xFF) * opacity));
const unsigned int colorEnd = Renderer::convertColor(mColorShiftEnd & 0xFFFFFF00 |
(unsigned char)((mColorShiftEnd & 0xFF) * opacity));
mVertices[0].col = color;
mVertices[1].col = mColorGradientHorizontal ? colorEnd : color;
@ -325,23 +349,23 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
Transform4x4f trans = parentTrans * getTransform();
Renderer::setMatrix(trans);
if(mTexture && mOpacity > 0)
{
if (mTexture && mOpacity > 0) {
if (Settings::getInstance()->getBool("DebugImage")) {
Vector2f targetSizePos = (mTargetSize - mSize) * mOrigin * -1;
Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(), mTargetSize.y(), 0xFF000033, 0xFF000033);
Renderer::drawRect(targetSizePos.x(), targetSizePos.y(), mTargetSize.x(),
mTargetSize.y(), 0xFF000033, 0xFF000033);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0x00000033, 0x00000033);
}
if(mTexture->isInitialized())
{
// actually draw the image
if (mTexture->isInitialized()) {
// Actually draw the image.
// 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 'jump' in
// when it finally loads
// texture is bound in this case but we want to handle a fade so it doesn't just
// 'jump' in when it finally loads.
fadeIn(mTexture->bind());
Renderer::drawTriangleStrips(&mVertices[0], 4);
}else{
}
else {
LOG(LogError) << "Image texture is not initialized!";
mTexture.reset();
}
@ -352,33 +376,27 @@ void ImageComponent::render(const Transform4x4f& parentTrans)
void ImageComponent::fadeIn(bool textureLoaded)
{
if (!mForceLoad)
{
if (!textureLoaded)
{
// Start the fade if this is the first time we've encountered the unloaded texture
if (!mFading)
{
// Start with a zero opacity and flag it as fading
if (!mForceLoad) {
if (!textureLoaded) {
// Start the fade if this is the first time we've encountered the unloaded texture.
if (!mFading) {
// Start with a zero opacity and flag it as fading.
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 value is not
// that important
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
// value is not that important.
int opacity = mFadeOpacity + 255 / 15;
// See if we've finished fading
if (opacity >= 255)
{
// See if we've finished fading.
if (opacity >= 255) {
mFadeOpacity = 255;
mFading = false;
}
else
{
else {
mFadeOpacity = (unsigned char)opacity;
}
updateColors();
@ -391,20 +409,22 @@ bool ImageComponent::hasImage()
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;
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");
if (!elem)
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"))
setResize(elem->get<Vector2f>("size") * scale);
else if (elem->has("maxSize"))
@ -416,20 +436,16 @@ void ImageComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const s
if (elem->has("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"));
setImage(elem->get<std::string>("path"), tile);
}
if(properties & COLOR)
{
if (properties & COLOR) {
if (elem->has("color"))
setColorShift(elem->get<unsigned int>("color"));
if (elem->has("colorEnd"))
setColorShiftEnd(elem->get<unsigned int>("colorEnd"));
if (elem->has("gradientType"))
setColorGradientHorizontal(!(elem->get<std::string>("gradientType").compare("horizontal")));
}

View file

@ -1,3 +1,9 @@
//
// ImageComponent.h
//
// Handles images: loading, resizing, cropping, color shifting etc.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
#define ES_CORE_COMPONENTS_IMAGE_COMPONENT_H
@ -16,7 +22,8 @@ public:
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
// as tiling, creates vertices accordingly).
void setImage(std::string path, bool tile = false);
// Loads an image from memory.
void setImage(const char* image, size_t length, bool tile = false);
@ -26,8 +33,9 @@ public:
void onSizeChanged() 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.
// If both are non-zero, potentially break the aspect ratio. If both are zero, no resizing.
// Resize the image to fit this size. If one axis is zero, scale that axis to maintain
// aspect ratio. If both are non-zero, potentially break the aspect ratio. If both are
// zero, don't do any resizing.
// Can be set before or after an image is loaded.
// setMaxSize() and setResize() are mutually exclusive.
void setResize(float width, float height);
@ -44,7 +52,7 @@ public:
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.
void cropLeft(float percent);
void cropTop(float percent);
@ -61,9 +69,11 @@ public:
void setFlipX(bool flip); // Mirror on the X 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.
// May be different than drawn size (use getSize() for that).
Vector2i getTextureSize() const;
Vector2f getSize() const override;
@ -72,11 +82,13 @@ public:
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;
std::shared_ptr<TextureResource> getTexture() { return mTexture; };
private:
Vector2f mTargetSize;

View file

@ -1,3 +1,9 @@
//
// ImageGridComponent.cpp
//
// X*Y image grid, used by GridGameListView.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
#define ES_CORE_COMPONENTS_IMAGE_GRID_COMPONENT_H
@ -10,21 +16,18 @@
#define EXTRAITEMS 2
enum ScrollDirection
{
enum ScrollDirection {
SCROLL_VERTICALLY,
SCROLL_HORIZONTALLY
};
enum ImageSource
{
enum ImageSource {
THUMBNAIL,
IMAGE,
MARQUEE
};
struct ImageGridData
{
struct ImageGridData {
std::string texturePath;
};
@ -58,10 +61,12 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) 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;
void onSizeChanged() override;
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
{ mCursorChangedCallback = func; }
ImageSource getImageSource() { return mImageSource; };
@ -69,23 +74,23 @@ protected:
virtual void onCursorChanged(const CursorState& state) override;
private:
// TILES
// Tiles.
void buildTiles();
void updateTiles(bool ascending = true, bool allowAnimation = true, bool updateSelectedState = true);
void updateTiles(bool ascending = true, bool allowAnimation = true,
bool updateSelectedState = true);
void updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState);
void calcGridDimension();
bool isScrollLoop();
bool isVertical() { return mScrollDirection == SCROLL_VERTICALLY; };
// IMAGES & ENTRIES
// Images and entries.
bool mEntriesDirty;
int mLastCursor;
std::string mDefaultGameTexture;
std::string mDefaultFolderTexture;
// TILES
// Tiles.
bool mLastRowPartial;
Vector2f mAutoLayout;
float mAutoLayoutZoom;
@ -102,7 +107,7 @@ private:
float mCamera;
float mCameraDirection;
// MISCELLANEOUS
// Miscellaneous.
bool mAnimate;
bool mCenterSelection;
bool mScrollLoop;
@ -114,7 +119,8 @@ private:
template<typename T>
ImageGridComponent<T>::ImageGridComponent(Window* window) : IList<ImageGridData, T>(window)
{
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
mCamera = 0.0;
mCameraDirection = 1.0;
@ -156,8 +162,7 @@ void ImageGridComponent<T>::add(const std::string& name, const std::string& imag
template<typename T>
bool ImageGridComponent<T>::input(InputConfig* config, Input input)
{
if(input.value != 0)
{
if (input.value != 0) {
int idx = isVertical() ? 0 : 1;
Vector2i dir = Vector2i::Zero();
@ -170,19 +175,18 @@ bool ImageGridComponent<T>::input(InputConfig* config, Input input)
else if (config->isMappedLike("right", input))
dir[0 ^ idx] = 1;
if(dir != Vector2i::Zero())
{
if (dir != Vector2i::Zero()) {
if (isVertical())
listInput(dir.x() + dir.y() * mGridDimension.x());
else
listInput(dir.x() + dir.y() * mGridDimension.y());
return true;
}
}else{
if(config->isMappedLike("up", input) || config->isMappedLike("down", input) || config->isMappedLike("left", input) || config->isMappedLike("right", input))
{
stopScrolling();
}
else {
if (config->isMappedLike("up", input) || config->isMappedLike("down", input) ||
config->isMappedLike("left", input) || config->isMappedLike("right", input))
stopScrolling();
}
return GuiComponent::input(config, input);
@ -204,33 +208,34 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
Transform4x4f trans = getTransform() * parentTrans;
Transform4x4f tileTrans = trans;
float offsetX = isVertical() ? 0.0f : mCamera * mCameraDirection * (mTileSize.x() + mMargin.x());
float offsetY = isVertical() ? mCamera * mCameraDirection * (mTileSize.y() + mMargin.y()) : 0.0f;
float offsetX = isVertical() ? 0.0f : mCamera * mCameraDirection *
(mTileSize.x() + mMargin.x());
float offsetY = isVertical() ? mCamera * mCameraDirection *
(mTileSize.y() + mMargin.y()) : 0.0f;
tileTrans.translate(Vector3f(offsetX, offsetY, 0.0));
if(mEntriesDirty)
{
if (mEntriesDirty) {
updateTiles();
mEntriesDirty = false;
}
// Create a clipRect to hide tiles used to buffer texture loading
// Create a clipRect to hide tiles used to buffer texture loading.
float scaleX = trans.r0().x();
float scaleY = trans.r1().y();
Vector2i pos((int)Math::round(trans.translation()[0]), (int)Math::round(trans.translation()[1]));
Vector2i size((int)Math::round(mSize.x() * scaleX), (int)Math::round(mSize.y() * scaleY));
Vector2i pos((int)Math::round(trans.translation()[0]),
(int)Math::round(trans.translation()[1]));
Vector2i size((int)Math::round(mSize.x() * scaleX),
(int)Math::round(mSize.y() * scaleY));
Renderer::pushClipRect(pos, size);
// Render all the tiles but the selected one
std::shared_ptr<GridTileComponent> selectedTile = NULL;
for(auto it = mTiles.begin(); it != mTiles.end(); it++)
{
// Render all the tiles but the selected one.
std::shared_ptr<GridTileComponent> selectedTile = nullptr;
for (auto it = mTiles.begin(); it != mTiles.end(); it++) {
std::shared_ptr<GridTileComponent> tile = (*it);
// If it's the selected image, keep it for later, otherwise render it now
// If it's the selected image, keep it for later, otherwise render it now.
if (tile->isSelected())
selectedTile = tile;
else
@ -239,8 +244,8 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
Renderer::popClipRect();
// Render the selected image on top of the others
if (selectedTile != NULL)
// Render the selected image on top of the others.
if (selectedTile != nullptr)
selectedTile->render(tileTrans);
listRenderTitleOverlay(trans);
@ -249,24 +254,27 @@ void ImageGridComponent<T>::render(const Transform4x4f& parentTrans)
}
template<typename T>
void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& element, unsigned int properties)
{
// Apply theme to GuiComponent but not size property, which will be applied at the end of this function
// Apply theme to GuiComponent but not the size property, which will be applied
// at the end of this function.
GuiComponent::applyTheme(theme, view, element, properties ^ ThemeFlags::SIZE);
// Keep the theme pointer to apply it on the tiles later on
// Keep the theme pointer to apply it on the tiles later on.
mTheme = theme;
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
Vector2f screen = Vector2f((float)Renderer::getScreenWidth(),
(float)Renderer::getScreenHeight());
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "imagegrid");
if (elem)
{
if (elem) {
if (elem->has("margin"))
mMargin = elem->get<Vector2f>("margin") * screen;
if (elem->has("padding"))
mPadding = elem->get<Vector4f>("padding") * Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
mPadding = elem->get<Vector4f>("padding") *
Vector4f(screen.x(), screen.y(), screen.x(), screen.y());
if (elem->has("autoLayout"))
mAutoLayout = elem->get<Vector2f>("autoLayout");
@ -274,8 +282,7 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("autoLayoutSelectedZoom"))
mAutoLayoutZoom = elem->get<float>("autoLayoutSelectedZoom");
if (elem->has("imageSource"))
{
if (elem->has("imageSource")) {
auto direction = elem->get<std::string>("imageSource");
if (direction == "image")
mImageSource = IMAGE;
@ -284,14 +291,15 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
else
mImageSource = THUMBNAIL;
}
else
else {
mImageSource = THUMBNAIL;
}
if (elem->has("scrollDirection"))
mScrollDirection = (ScrollDirection)(elem->get<std::string>("scrollDirection") == "horizontal");
mScrollDirection = (ScrollDirection)(elem->
get<std::string>("scrollDirection") == "horizontal");
if (elem->has("centerSelection"))
{
if (elem->has("centerSelection")) {
mCenterSelection = (elem->get<bool>("centerSelection"));
if (elem->has("scrollLoop"))
@ -303,42 +311,38 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
else
mAnimate = true;
if (elem->has("gameImage"))
{
if (elem->has("gameImage")) {
std::string path = elem->get<std::string>("gameImage");
if (!ResourceManager::getInstance()->fileExists(path))
if (!ResourceManager::getInstance()->fileExists(path)) {
LOG(LogWarning) << "Could not replace default game image, check path: " << path;
else
{
}
else {
std::string oldDefaultGameTexture = mDefaultGameTexture;
mDefaultGameTexture = path;
// mEntries are already loaded at this point,
// so we need to update them with new game image texture
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
{
// mEntries are already loaded at this point, so we need to
// update them with the new game image texture.
for (auto it = mEntries.begin(); it != mEntries.end(); it++) {
if ((*it).data.texturePath == oldDefaultGameTexture)
(*it).data.texturePath = mDefaultGameTexture;
}
}
}
if (elem->has("folderImage"))
{
if (elem->has("folderImage")) {
std::string path = elem->get<std::string>("folderImage");
if (!ResourceManager::getInstance()->fileExists(path))
if (!ResourceManager::getInstance()->fileExists(path)) {
LOG(LogWarning) << "Could not replace default folder image, check path: " << path;
else
{
}
else {
std::string oldDefaultFolderTexture = mDefaultFolderTexture;
mDefaultFolderTexture = path;
// mEntries are already loaded at this point,
// so we need to update them with new folder image texture
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
{
// mEntries are already loaded at this point, so we need to
// update them with new folder image texture.
for (auto it = mEntries.begin(); it != mEntries.end(); it++) {
if ((*it).data.texturePath == oldDefaultFolderTexture)
(*it).data.texturePath = mDefaultFolderTexture;
}
@ -346,18 +350,18 @@ void ImageGridComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
}
}
// We still need to manually get the grid tile size here,
// so we can recalculate the new grid dimension, and THEN (re)build the tiles
// We still need to manually get the grid tile size here, so we can recalculate
// the new grid dimension, and THEN (re)build the tiles.
elem = theme->getElement(view, "default", "gridtile");
mTileSize = elem && elem->has("size") ?
elem->get<Vector2f>("size") * screen :
GridTileComponent::getDefaultTileSize();
// Apply size property, will trigger a call to onSizeChanged() which will build the tiles
// Apply size property which will trigger a call to onSizeChanged() which will build the tiles.
GuiComponent::applyTheme(theme, view, element, ThemeFlags::SIZE);
// Trigger the call manually if the theme have no "imagegrid" element
// Trigger the call manually if the theme have no "imagegrid" element.
if (!elem)
buildTiles();
}
@ -372,20 +376,16 @@ void ImageGridComponent<T>::onSizeChanged()
template<typename T>
void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
{
if (mLastCursor == mCursor)
{
if (mLastCursor == mCursor) {
if (state == CURSOR_STOPPED && mCursorChangedCallback)
mCursorChangedCallback(state);
return;
}
bool direction = mCursor >= mLastCursor;
int diff = direction ? mCursor - mLastCursor : mLastCursor - mCursor;
if (isScrollLoop() && diff == mEntries.size() - 1)
{
direction = !direction;
}
int oldStart = mStartPosition;
@ -405,15 +405,13 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
float startPos = 0;
float endPos = 1;
if (((GuiComponent*)this)->isAnimationPlaying(2))
{
if (((GuiComponent*)this)->isAnimationPlaying(2)) {
startPos = 0;
((GuiComponent*)this)->cancelAnimation(2);
updateTiles(direction, false, false);
}
if (mAnimate) {
std::shared_ptr<GridTileComponent> oldTile = nullptr;
std::shared_ptr<GridTileComponent> newTile = nullptr;
@ -452,19 +450,20 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
int firstVisibleCol = mStartPosition / dimOpposite;
if ((col < centralCol || (col == 0 && col == centralCol)) && !mCenterSelection)
if ((col < centralCol || (col == 0 && col == centralCol)) && !mCenterSelection) {
mStartPosition = 0;
else if ((col - centralCol) > lastScroll && !mCenterSelection && !isScrollLoop())
}
else if ((col - centralCol) > lastScroll && !mCenterSelection && !isScrollLoop()) {
mStartPosition = lastScroll * dimOpposite;
else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) || col == firstVisibleCol + centralCol)
{
}
else if ((maxCentralCol != centralCol && col == firstVisibleCol + maxCentralCol) ||
col == firstVisibleCol + centralCol) {
if (col == firstVisibleCol + maxCentralCol)
mStartPosition = (col - maxCentralCol) * dimOpposite;
else
mStartPosition = (col - centralCol) * dimOpposite;
}
else
{
else {
if (oldCol == firstVisibleCol + maxCentralCol)
mStartPosition = (col - maxCentralCol) * dimOpposite;
else
@ -477,8 +476,7 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
mCameraDirection = direction ? -1.0f : 1.0f;
mCamera = 0;
if (lastCursor < 0 || !mAnimate)
{
if (lastCursor < 0 || !mAnimate) {
updateTiles(direction, mAnimate && (lastCursor >= 0 || isScrollLoop()));
if (mCursorChangedCallback)
@ -492,15 +490,13 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
bool moveCamera = (oldStart != mStartPosition);
auto func = [this, startPos, endPos, moveCamera](float t)
{
auto func = [this, startPos, endPos, moveCamera](float t) {
if (!moveCamera)
return;
t -= 1; // cubic ease out
float pct = Math::lerp(0, 1, t*t*t + 1);
t = startPos * (1.0f - pct) + endPos * pct;
mCamera = t;
};
@ -511,7 +507,7 @@ void ImageGridComponent<T>::onCursorChanged(const CursorState& state)
}
// Create and position tiles (mTiles)
// Create and position tiles (mTiles).
template<typename T>
void ImageGridComponent<T>::buildTiles()
{
@ -520,45 +516,44 @@ void ImageGridComponent<T>::buildTiles()
calcGridDimension();
if (mCenterSelection)
{
int dimScrollable = (isVertical() ? mGridDimension.y() : mGridDimension.x()) - 2 * EXTRAITEMS;
if (mCenterSelection) {
int dimScrollable = (isVertical() ? mGridDimension.y() :
mGridDimension.x()) - 2 * EXTRAITEMS;
mStartPosition -= (int) Math::floorf(dimScrollable / 2.0f);
}
Vector2f tileDistance = mTileSize + mMargin;
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
{
auto x = (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1)) - mPadding.x() - mPadding.z()) / (int) mAutoLayout.x();
auto y = (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1)) - mPadding.y() - mPadding.w()) / (int) mAutoLayout.y();
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0) {
auto x = (mSize.x() - (mMargin.x() * (mAutoLayout.x() - 1)) - mPadding.x() -
mPadding.z()) / (int) mAutoLayout.x();
auto y = (mSize.y() - (mMargin.y() * (mAutoLayout.y() - 1)) - mPadding.y() -
mPadding.w()) / (int) mAutoLayout.y();
mTileSize = Vector2f(x, y);
tileDistance = mTileSize + mMargin;
}
bool vert = isVertical();
Vector2f startPosition = mTileSize / 2;
startPosition += mPadding.v2();
int X, Y;
int X;
int Y;
// Layout tile size and position
for (int y = 0; y < (vert ? mGridDimension.y() : mGridDimension.x()); y++)
{
for (int x = 0; x < (vert ? mGridDimension.x() : mGridDimension.y()); x++)
{
// Create tiles
// Layout tile size and position.
for (int y = 0; y < (vert ? mGridDimension.y() : mGridDimension.x()); y++) {
for (int x = 0; x < (vert ? mGridDimension.x() : mGridDimension.y()); x++) {
// Create tiles.
auto tile = std::make_shared<GridTileComponent>(mWindow);
// In Vertical mod, tiles are ordered from left to right, then from top to bottom
// In Horizontal mod, tiles are ordered from top to bottom, then from left to right
// In Vertical mod, tiles are ordered from left to right, then from top to bottom.
// In Horizontal mod, tiles are ordered from top to bottom, then from left to right.
X = vert ? x : y - EXTRAITEMS;
Y = vert ? y - EXTRAITEMS : x;
tile->setPosition(X * tileDistance.x() + startPosition.x(), Y * tileDistance.y() + startPosition.y());
tile->setPosition(X * tileDistance.x() + startPosition.x(), Y *
tileDistance.y() + startPosition.y());
tile->setOrigin(0.5f, 0.5f);
tile->setImage("");
@ -574,16 +569,15 @@ void ImageGridComponent<T>::buildTiles()
}
template<typename T>
void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, bool updateSelectedState)
void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation,
bool updateSelectedState)
{
if (!mTiles.size())
return;
// Stop updating the tiles at highest scroll speed
if (mScrollTier == 3)
{
for (int ti = 0; ti < (int)mTiles.size(); ti++)
{
// Stop updating the tiles at highest scroll speed.
if (mScrollTier == 3) {
for (int ti = 0; ti < (int)mTiles.size(); ti++) {
std::shared_ptr<GridTileComponent> tile = mTiles.at(ti);
tile->setSelected(false);
@ -593,16 +587,15 @@ void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, boo
return;
}
// Temporary store previous texture so they can't be unloaded
// Temporary store previous texture so they can't be unloaded.
std::vector<std::shared_ptr<TextureResource>> previousTextures;
for (int ti = 0; ti < (int)mTiles.size(); ti++)
{
for (int ti = 0; ti < (int)mTiles.size(); ti++) {
std::shared_ptr<GridTileComponent> tile = mTiles.at(ti);
previousTextures.push_back(tile->getTexture());
}
// If going down, update from top to bottom
// If going up, update from bottom to top
// If going down, update from top to bottom.
// If going up, update from bottom to top.
int scrollDirection = ascending ? 1 : -1;
int ti = ascending ? 0 : (int)mTiles.size() - 1;
int end = ascending ? (int)mTiles.size() : -1;
@ -610,9 +603,8 @@ void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, boo
img -= EXTRAITEMS * (isVertical() ? mGridDimension.x() : mGridDimension.y());
// Update the tiles
while (ti != end)
{
// Update the tiles.
while (ti != end) {
updateTileAtPos(ti, img, allowAnimation, updateSelectedState);
ti += scrollDirection;
@ -626,29 +618,27 @@ void ImageGridComponent<T>::updateTiles(bool ascending, bool allowAnimation, boo
}
template<typename T>
void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos, bool allowAnimation, bool updateSelectedState)
void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos,
bool allowAnimation, bool updateSelectedState)
{
std::shared_ptr<GridTileComponent> tile = mTiles.at(tilePos);
if(isScrollLoop())
{
if (isScrollLoop()) {
if (imgPos < 0)
imgPos += mEntries.size();
else if (imgPos >= size())
imgPos -= mEntries.size();
}
// If we have more tiles than we have to display images on screen, hide them
if(imgPos < 0 || imgPos >= size() || tilePos < 0 || tilePos >= (int) mTiles.size()) // Same for tiles out of the buffer
{
// If we have more tiles than we have to display images on screen, hide them.
// Same for tiles out of the buffer.
if (imgPos < 0 || imgPos >= size() || tilePos < 0 || tilePos >= (int) mTiles.size()) {
if (updateSelectedState)
tile->setSelected(false, allowAnimation);
tile->reset();
tile->setVisible(false);
}
else
{
else {
tile->setVisible(true);
std::string imagePath = mEntries.at(imgPos).data.texturePath;
@ -660,10 +650,8 @@ void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos, bool allowA
else
tile->setImage(mDefaultGameTexture);
if (updateSelectedState)
{
if (imgPos == mCursor && mCursor != mLastCursor)
{
if (updateSelectedState) {
if (imgPos == mCursor && mCursor != mLastCursor) {
int dif = mCursor - tilePos;
int idx = mLastCursor - dif;
@ -673,19 +661,21 @@ void ImageGridComponent<T>::updateTileAtPos(int tilePos, int imgPos, bool allowA
Vector3f pos = mTiles.at(idx)->getBackgroundPosition();
tile->setSelected(true, allowAnimation, &pos);
}
else
else {
tile->setSelected(imgPos == mCursor, allowAnimation);
}
}
}
}
// Calculate how much tiles of size mTileSize we can fit in a grid of size mSize using a margin of size mMargin
// Calculate how much tiles of size mTileSize we can fit in a grid of size mSize using
// a margin of size mMargin.
template<typename T>
void ImageGridComponent<T>::calcGridDimension()
{
// GRID_SIZE = COLUMNS * TILE_SIZE + (COLUMNS - 1) * MARGIN
// <=> COLUMNS = (GRID_SIZE + MARGIN) / (TILE_SIZE + MARGIN)
// grid_size = columns * tile_size + (columns - 1) * margin
// <=> columns = (grid_size + margin) / (tile_size + margin)
Vector2f gridDimension = (mSize + mMargin) / (mTileSize + mMargin);
if (mAutoLayout.x() != 0 && mAutoLayout.y() != 0)
@ -693,10 +683,10 @@ void ImageGridComponent<T>::calcGridDimension()
mLastRowPartial = Math::floorf(gridDimension.y()) != gridDimension.y();
// Ceil y dim so we can display partial last row
// Ceil y dim so we can display partial last row.
mGridDimension = Vector2i(gridDimension.x(), Math::ceilf(gridDimension.y()));
// Grid dimension validation
// Grid dimension validation.
if (mGridDimension.x() < 1) {
LOG(LogError) << "Theme defined grid X dimension below 1";
}
@ -704,7 +694,7 @@ void ImageGridComponent<T>::calcGridDimension()
LOG(LogError) << "Theme defined grid Y dimension below 1";
}
// Add extra tiles to both sides : Add EXTRAITEMS before, EXTRAITEMS after
// Add extra tiles to both sides: Add EXTRAITEMS before, EXTRAITEMS after.
if (isVertical())
mGridDimension.y() += 2 * EXTRAITEMS;
else

View file

@ -1,14 +1,26 @@
//
// NinePatchComponent.cpp
//
// Breaks up an image into 3x3 patches to accomodate resizing without distortions.
//
#include "components/NinePatchComponent.h"
#include "resources/TextureResource.h"
#include "Log.h"
#include "ThemeData.h"
NinePatchComponent::NinePatchComponent(Window* window, const std::string& path, unsigned int edgeColor, unsigned int centerColor) : GuiComponent(window),
NinePatchComponent::NinePatchComponent(
Window* window,
const std::string& path,
unsigned int edgeColor,
unsigned int centerColor)
: GuiComponent(window),
mCornerSize(16, 16),
mEdgeColor(edgeColor), mCenterColor(centerColor),
mEdgeColor(edgeColor),
mCenterColor(centerColor),
mPath(path),
mVertices(NULL)
mVertices(nullptr)
{
if (!mPath.empty())
buildVertices();
@ -16,7 +28,7 @@ NinePatchComponent::NinePatchComponent(Window* window, const std::string& path,
NinePatchComponent::~NinePatchComponent()
{
if (mVertices != NULL)
if (mVertices != nullptr)
delete[] mVertices;
}
@ -34,36 +46,37 @@ void NinePatchComponent::updateColors()
void NinePatchComponent::buildVertices()
{
if(mVertices != NULL)
if (mVertices != nullptr)
delete[] mVertices;
mTexture = TextureResource::get(mPath);
if(mTexture->getSize() == Vector2i::Zero())
{
mVertices = NULL;
if (mTexture->getSize() == Vector2i::Zero()) {
mVertices = nullptr;
LOG(LogWarning) << "NinePatchComponent missing texture!";
return;
}
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 imgSizeY[3] = { mCornerSize.y(), mSize.y() - mCornerSize.y() * 2, mCornerSize.y()};
const float imgPosX[3] = { 0, imgSizeX[0], imgSizeX[0] + imgSizeX[1]};
const float imgPosY[3] = { 0, imgSizeY[0], imgSizeY[0] + imgSizeY[1]};
//the "1 +" in posY and "-" in sizeY is to deal with texture coordinates having a bottom 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
// left corner origin vs. verticies having a top left origin.
const float texSizeX[3] = { mCornerSize.x() / texSize.x(), (texSize.x() - mCornerSize.x() * 2) / texSize.x(), mCornerSize.x() / texSize.x() };
const float texSizeY[3] = { -mCornerSize.y() / texSize.y(), -(texSize.y() - mCornerSize.y() * 2) / texSize.y(), -mCornerSize.y() / texSize.y() };
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;
for(int slice = 0; slice < 9; slice++)
{
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]);
@ -76,11 +89,11 @@ void NinePatchComponent::buildVertices()
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 };
// round vertices
// Round vertices.
for (int i = 1; i < 5; ++i)
mVertices[v + i].pos.round();
// make duplicates of first and last vertex so this can be rendered as a triangle strip
// 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];
@ -97,8 +110,7 @@ void NinePatchComponent::render(const Transform4x4f& parentTrans)
Transform4x4f trans = parentTrans * getTransform();
if(mTexture && mVertices != NULL)
{
if (mTexture && mVertices != nullptr) {
Renderer::setMatrix(trans);
mTexture->bind();
@ -153,13 +165,14 @@ void NinePatchComponent::setCenterColor(unsigned int centerColor)
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);
using namespace ThemeFlags;
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "ninepatch");
if (!elem)
return;

View file

@ -1,3 +1,9 @@
//
// NinePatchComponent.h
//
// Breaks up an image into 3x3 patches to accomodate resizing without distortions.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
#define ES_CORE_COMPONENTS_NINE_PATCH_COMPONENT_H
@ -7,13 +13,14 @@
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:
// ___________
// |_1_|_2_|_3_|
// |_4_|_5_|_6_|
// |_7_|_8_|_9_|
//
// 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).
// The center (5) will be stretched.
@ -21,20 +28,25 @@ class TextureResource;
class NinePatchComponent : public GuiComponent
{
public:
NinePatchComponent(Window* window, const std::string& path = "", unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF);
NinePatchComponent(Window* window, const std::string& path = "",
unsigned int edgeColor = 0xFFFFFFFF, unsigned int centerColor = 0xFFFFFFFF);
virtual ~NinePatchComponent();
void render(const Transform4x4f& parentTrans) 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 setEdgeColor(unsigned int edgeColor); // 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.
// Apply a color shift to the "edge" parts 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;
void setCornerSize(int sizeX, int sizeY);

View file

@ -1,14 +1,28 @@
//
// 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 "math/Vector2i.h"
#include "renderers/Renderer.h"
#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_SPEED 50 // ms between scrolls
#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_SPEED 100 // ms between scrolls.
ScrollableContainer::ScrollableContainer(Window* window) : GuiComponent(window),
mAutoScrollDelay(0), mAutoScrollSpeed(0), mAutoScrollAccumulator(0), mScrollPos(0, 0), mScrollDir(0, 0), mAutoScrollResetAccumulator(0)
ScrollableContainer::ScrollableContainer(
Window* window)
: GuiComponent(window),
mAutoScrollDelay(0),
mAutoScrollSpeed(0),
mAutoScrollAccumulator(0),
mScrollPos(0, 0),
mScrollDir(0, 0),
mAutoScrollResetAccumulator(0)
{
}
@ -22,7 +36,8 @@ void ScrollableContainer::render(const Transform4x4f& parentTrans)
Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y());
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);
@ -30,19 +45,18 @@ void ScrollableContainer::render(const Transform4x4f& parentTrans)
Renderer::setMatrix(trans);
GuiComponent::renderChildren(trans);
Renderer::popClipRect();
}
void ScrollableContainer::setAutoScroll(bool autoScroll)
{
if(autoScroll)
{
if (autoScroll) {
mScrollDir = Vector2f(0, 1);
mAutoScrollDelay = AUTO_SCROLL_DELAY;
mAutoScrollSpeed = AUTO_SCROLL_SPEED;
reset();
}else{
}
else {
mScrollDir = Vector2f(0, 0);
mAutoScrollDelay = 0;
mAutoScrollSpeed = 0;
@ -62,43 +76,38 @@ void ScrollableContainer::setScrollPos(const Vector2f& pos)
void ScrollableContainer::update(int deltaTime)
{
if(mAutoScrollSpeed != 0)
{
if (mAutoScrollSpeed != 0) {
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());
while(mAutoScrollAccumulator >= mAutoScrollSpeed)
{
while (mAutoScrollAccumulator >= mAutoScrollSpeed) {
mScrollPos += mScrollDir;
mAutoScrollAccumulator -= mAutoScrollSpeed;
}
}
//clip scrolling within bounds
// Clip scrolling within bounds.
if (mScrollPos.x() < 0)
mScrollPos[0] = 0;
if (mScrollPos.y() < 0)
mScrollPos[1] = 0;
const Vector2f contentSize = getContentSize();
if(mScrollPos.x() + getSize().x() > contentSize.x())
{
if (mScrollPos.x() + getSize().x() > contentSize.x()) {
mScrollPos[0] = contentSize.x() - getSize().x();
mAtEnd = true;
}
if(contentSize.y() < getSize().y())
{
if (contentSize.y() < getSize().y()) {
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();
mAtEnd = true;
}
if(mAtEnd)
{
if (mAtEnd) {
mAutoScrollResetAccumulator += deltaTime;
if (mAutoScrollResetAccumulator >= AUTO_SCROLL_RESET_DELAY)
reset();
@ -107,12 +116,11 @@ void ScrollableContainer::update(int 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 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 bottomRight = mChildren.at(i)->getSize() + pos;
if (bottomRight.x() > max.x())

View file

@ -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
#ifndef ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
#define ES_CORE_COMPONENTS_SCROLLABLE_CONTAINER_H
@ -22,8 +29,8 @@ private:
Vector2f mScrollPos;
Vector2f mScrollDir;
int mAutoScrollDelay; // ms to wait before starting to autoscroll
int mAutoScrollSpeed; // ms to wait before scrolling down by mScrollDir
int mAutoScrollDelay; // ms to wait before starting to autoscroll.
int mAutoScrollSpeed; // ms to wait before scrolling down by mScrollDir.
int mAutoScrollAccumulator;
bool mAtEnd;
int mAutoScrollResetAccumulator;

View file

@ -1,3 +1,9 @@
//
// SliderComponent.cpp
//
// Slider to set value in a predefined range.
//
#include "components/SliderComponent.h"
#include "resources/Font.h"
@ -5,12 +11,23 @@
#define MOVE_REPEAT_DELAY 500
#define MOVE_REPEAT_RATE 40
SliderComponent::SliderComponent(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)
SliderComponent::SliderComponent(
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);
// some sane default value
// Some sane default value.
mValue = (max + min) / 2;
mKnob.setOrigin(0.5f, 0.5f);
@ -21,8 +38,7 @@ SliderComponent::SliderComponent(Window* window, float min, float max, float inc
bool SliderComponent::input(InputConfig* config, Input input)
{
if(config->isMappedLike("left", input))
{
if (config->isMappedLike("left", input)) {
if (input.value)
setValue(mValue - mSingleIncrement);
@ -30,8 +46,7 @@ bool SliderComponent::input(InputConfig* config, Input input)
mMoveAccumulator = -MOVE_REPEAT_DELAY;
return true;
}
if(config->isMappedLike("right", input))
{
if (config->isMappedLike("right", input)) {
if (input.value)
setValue(mValue + mSingleIncrement);
@ -45,11 +60,9 @@ bool SliderComponent::input(InputConfig* config, Input input)
void SliderComponent::update(int deltaTime)
{
if(mMoveRate != 0)
{
if (mMoveRate != 0) {
mMoveAccumulator += deltaTime;
while(mMoveAccumulator >= MOVE_REPEAT_RATE)
{
while (mMoveAccumulator >= MOVE_REPEAT_RATE) {
setValue(mValue + mMoveRate);
mMoveAccumulator -= MOVE_REPEAT_RATE;
}
@ -63,17 +76,19 @@ void SliderComponent::render(const Transform4x4f& parentTrans)
Transform4x4f trans = parentTrans * getTransform();
Renderer::setMatrix(trans);
// render suffix
// Render suffix.
if (mValueCache)
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;
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);
GuiComponent::renderChildren(trans);
@ -105,9 +120,8 @@ void SliderComponent::onSizeChanged()
void SliderComponent::onValueChanged()
{
// update suffix textcache
if(mFont)
{
// Update suffix textcache.
if (mFont) {
std::stringstream ss;
ss << std::fixed;
ss.precision(0);
@ -124,14 +138,17 @@ void SliderComponent::onValueChanged()
const std::string max = ss.str();
Vector2f textSize = mFont->sizeText(max);
mValueCache = std::shared_ptr<TextCache>(mFont->buildTextCache(val, mSize.x() - textSize.x(), (mSize.y() - textSize.y()) / 2, 0x777777FF));
mValueCache->metrics.size[0] = textSize.x(); // fudge the width
mValueCache = std::shared_ptr<TextCache>(mFont->buildTextCache(val, mSize.x() -
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);
float lineLength = mSize.x() - mKnob.getSize().x() - (mValueCache ? mValueCache->metrics.size.x() + 4 : 0);
mKnob.setPosition(((mValue + mMin) / mMax) * lineLength + mKnob.getSize().x()/2, mSize.y() / 2);
float lineLength = mSize.x() - mKnob.getSize().x() -
(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()

View file

@ -1,3 +1,9 @@
//
// SliderComponent.h
//
// Slider to set value in a predefined range.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
#define ES_CORE_COMPONENTS_SLIDER_COMPONENT_H
@ -12,8 +18,14 @@ class TextCache;
class SliderComponent : public GuiComponent
{
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).
SliderComponent(Window* window, float min, float max, float increment, const std::string& suffix = "");
// 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).
SliderComponent(
Window* window,
float min,
float max,
float increment,
const std::string& suffix = "");
void setValue(float val);
float getValue();

View file

@ -1,20 +1,48 @@
//
// TextComponent.cpp
//
// Displays text.
//
#include "components/TextComponent.h"
#include "utils/StringUtil.h"
#include "Log.h"
#include "Settings.h"
TextComponent::TextComponent(Window* window) : GuiComponent(window),
mFont(Font::get(FONT_SIZE_MEDIUM)), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
mHorizontalAlignment(ALIGN_LEFT), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
TextComponent::TextComponent(
Window* window)
: GuiComponent(window),
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,
Vector3f pos, Vector2f size, unsigned int bgcolor) : GuiComponent(window),
mFont(NULL), mUppercase(false), mColor(0x000000FF), mAutoCalcExtent(true, true),
mHorizontalAlignment(align), mVerticalAlignment(ALIGN_CENTER), mLineSpacing(1.5f), mBgColor(0),
TextComponent::TextComponent(
Window* window,
const std::string& text,
const std::shared_ptr<Font>& font,
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);
@ -37,7 +65,7 @@ void TextComponent::setFont(const std::shared_ptr<Font>& font)
onTextChanged();
}
// Set the color of the font/text
// Set the color of the font/text.
void TextComponent::setColor(unsigned int color)
{
mColor = color;
@ -45,7 +73,7 @@ void TextComponent::setColor(unsigned int color)
onColorChanged();
}
// Set the color of the background box
// Set the color of the background box.
void TextComponent::setBackgroundColor(unsigned int color)
{
mBgColor = color;
@ -57,7 +85,7 @@ void TextComponent::setRenderBackground(bool render)
mRenderBackground = render;
}
// Scale the opacity
// Scale the opacity.
void TextComponent::setOpacity(unsigned char opacity)
{
// This method is mostly called to do fading in-out of the Text component element.
@ -71,7 +99,6 @@ void TextComponent::setOpacity(unsigned char opacity)
mBgColor = (mBgColor & 0xFFFFFF00) | (unsigned char)bgo;
onColorChanged();
GuiComponent::setOpacity(opacity);
}
@ -99,18 +126,15 @@ void TextComponent::render(const Transform4x4f& parentTrans)
Transform4x4f trans = parentTrans * getTransform();
if (mRenderBackground)
{
if (mRenderBackground) {
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), mBgColor, mBgColor);
}
if(mTextCache)
{
if (mTextCache) {
const Vector2f& textSize = mTextCache->metrics.size;
float yOff = 0;
switch(mVerticalAlignment)
{
switch (mVerticalAlignment) {
case ALIGN_TOP:
yOff = 0;
break;
@ -125,9 +149,8 @@ void TextComponent::render(const Transform4x4f& parentTrans)
}
Vector3f off(0, yOff, 0);
if(Settings::getInstance()->getBool("DebugText"))
{
// draw the "textbox" area, what we are aligned within
if (Settings::getInstance()->getBool("DebugText")) {
// Draw the "textbox" area, what we are aligned within.
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), 0xFF000033, 0xFF000033);
}
@ -135,19 +158,22 @@ void TextComponent::render(const Transform4x4f& parentTrans)
trans.translate(off);
Renderer::setMatrix(trans);
// draw the text area, where the text actually is going
if(Settings::getInstance()->getBool("DebugText"))
{
switch(mHorizontalAlignment)
{
// Draw the text area, where the text actually is located.
if (Settings::getInstance()->getBool("DebugText")) {
switch (mHorizontalAlignment) {
case ALIGN_LEFT:
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
Renderer::drawRect(0.0f, 0.0f, mTextCache->metrics.size.x(),
mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
break;
case ALIGN_CENTER:
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
Renderer::drawRect((mSize.x() - mTextCache->metrics.size.x()) / 2.0f, 0.0f,
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
0x00000033, 0x00000033);
break;
case ALIGN_RIGHT:
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f, mTextCache->metrics.size.x(), mTextCache->metrics.size.y(), 0x00000033, 0x00000033);
Renderer::drawRect(mSize.x() - mTextCache->metrics.size.x(), 0.0f,
mTextCache->metrics.size.x(), mTextCache->metrics.size.y(),
0x00000033, 0x00000033);
break;
default:
break;
@ -159,14 +185,13 @@ void TextComponent::render(const Transform4x4f& parentTrans)
void TextComponent::calculateExtent()
{
if(mAutoCalcExtent.x())
{
if (mAutoCalcExtent.x()) {
mSize = mFont->sizeText(mUppercase ? Utils::String::toUpper(mText) : mText, mLineSpacing);
}else{
if(mAutoCalcExtent.y())
{
mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText) : mText, getSize().x(), mLineSpacing).y();
}
else {
if (mAutoCalcExtent.y())
mSize[1] = mFont->sizeWrappedText(mUppercase ? Utils::String::toUpper(mText)
: mText, getSize().x(), mLineSpacing).y();
}
}
@ -174,8 +199,7 @@ void TextComponent::onTextChanged()
{
calculateExtent();
if(!mFont || mText.empty())
{
if (!mFont || mText.empty()) {
mTextCache.reset();
return;
}
@ -186,22 +210,20 @@ void TextComponent::onTextChanged()
const bool isMultiline = (mSize.y() == 0 || mSize.y() > f->getHeight()*1.2f);
bool addAbbrev = false;
if(!isMultiline)
{
if (!isMultiline) {
size_t newline = text.find('\n');
text = text.substr(0, newline); // single line of text - stop at the first newline since it'll mess everything up
// 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;
}
Vector2f size = f->sizeText(text);
if(!isMultiline && mSize.x() && text.size() && (size.x() > mSize.x() || addAbbrev))
{
// abbreviate text
if (!isMultiline && mSize.x() && text.size() && (size.x() > mSize.x() || addAbbrev)) {
// Abbreviate text.
const std::string 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());
text.erase(newSize, text.size() - newSize);
size = f->sizeText(text);
@ -209,19 +231,21 @@ void TextComponent::onTextChanged()
text.append(abbrev);
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, 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));
mTextCache = std::shared_ptr<TextCache>(f->buildTextCache(text, 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()
{
if (mTextCache)
{
mTextCache->setColor(mColor);
}
}
void TextComponent::setHorizontalAlignment(Alignment align)
{
@ -250,7 +274,8 @@ std::string TextComponent::getValue() const
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);
@ -269,8 +294,7 @@ void TextComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const st
setRenderBackground(true);
}
if(properties & ALIGNMENT && elem->has("alignment"))
{
if (properties & ALIGNMENT && elem->has("alignment")) {
std::string str = elem->get<std::string>("alignment");
if (str == "left")
setHorizontalAlignment(ALIGN_LEFT);

View file

@ -1,3 +1,9 @@
//
// TextComponent.h
//
// Displays text.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_TEXT_COMPONENT_H
#define ES_CORE_COMPONENTS_TEXT_COMPONENT_H
@ -9,15 +15,24 @@ class ThemeData;
// Used to display text.
// 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)
// * (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.
// * (0, 0) - Will automatically calculate a size that fits
// the text on one line (expand horizontally).
// * (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
{
public:
TextComponent(Window* window);
TextComponent(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);
TextComponent(
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 setUppercase(bool uppercase);
@ -41,7 +56,8 @@ public:
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:
virtual void onTextChanged();

View file

@ -1,3 +1,9 @@
//
// VideoComponent.cpp
//
// Base class for playing videos.
//
#include "components/VideoComponent.h"
#include "resources/ResourceManager.h"
@ -28,26 +34,24 @@ void writeSubtitle(const char* gameName, const char* systemName, bool always)
{
FILE* file = fopen(getTitlePath().c_str(), "w");
int end = (int)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000));
if (always) {
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 (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);
}
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 = NULL;
file = nullptr;
}
void VideoComponent::setScreensaverMode(bool isScreensaver)
@ -55,8 +59,9 @@ void VideoComponent::setScreensaverMode(bool isScreensaver)
mScreensaverMode = isScreensaver;
}
VideoComponent::VideoComponent(Window* window) :
GuiComponent(window),
VideoComponent::VideoComponent(
Window* window)
: GuiComponent(window),
mStaticImage(window),
mVideoHeight(0),
mVideoWidth(0),
@ -69,69 +74,71 @@ VideoComponent::VideoComponent(Window* window) :
mTargetIsMax(false),
mTargetSize(0, 0)
{
// Setup the default configuration
// Setup the default configuration.
mConfig.showSnapshotDelay = false;
mConfig.showSnapshotNoVideo = false;
mConfig.startDelay = 0;
if (mWindow->getGuiStackSize() > 1) {
if (mWindow->getGuiStackSize() > 1)
topWindow(false);
}
std::string path = getTitleFolder();
if (!Utils::FileSystem::exists(path))
Utils::FileSystem::createDirectory(path);
}
VideoComponent::~VideoComponent()
{
// Stop any currently running video
// Stop any currently running video.
stopVideo();
// Delete subtitle file, if existing
// Delete subtitle file, if existing.
remove(getTitlePath().c_str());
}
void VideoComponent::onOriginChanged()
{
// Update the embeded static image
// Update the embeded static image.
mStaticImage.setOrigin(mOrigin);
}
void VideoComponent::onPositionChanged()
{
// Update the embeded static image
// Update the embeded static image.
mStaticImage.setPosition(mPosition);
}
void VideoComponent::onSizeChanged()
{
// Update the embeded static image
// Update the embeded static image.
mStaticImage.onSizeChanged();
}
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);
// Check that it's changed
// Check that it's changed.
if (fullPath == mVideoPath)
return !path.empty();
// Store the path
// Store the path.
mVideoPath = fullPath;
// If the file exists then set the new video
if (!fullPath.empty() && ResourceManager::getInstance()->fileExists(fullPath))
{
// Return true to show that we are going to attempt to play a video
// If the file exists then set the new video.
if (!fullPath.empty() && ResourceManager::getInstance()->fileExists(fullPath)) {
// Return true to show that we are going to attempt to play a video.
return true;
}
// Return false to show that no video will be displayed
// Return false to show that no video will be displayed.
return false;
}
void VideoComponent::setImage(std::string path)
{
// Check that the image has changed
// Check that the image has changed.
if (path == mStaticImagePath)
return;
@ -148,7 +155,7 @@ void VideoComponent::setDefaultVideo()
void VideoComponent::setOpacity(unsigned char opacity)
{
mOpacity = opacity;
// Update the embeded static image
// Update the embeded static image.
mStaticImage.setOpacity(opacity);
}
@ -162,39 +169,42 @@ void VideoComponent::render(const Transform4x4f& parentTrans)
Renderer::setMatrix(trans);
// Handle the case where the video is delayed
// Handle the case where the video is delayed.
handleStartDelay();
// Handle looping of the video
// Handle looping of the video.
handleLooping();
}
void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans)
{
// This is the case where the video is not currently being displayed. Work out
// if we need to display a static image
if ((mConfig.showSnapshotNoVideo && mVideoPath.empty()) || (mStartDelayed && mConfig.showSnapshotDelay))
{
// Display the static image instead
// if we need to display a static image.
if ((mConfig.showSnapshotNoVideo && mVideoPath.empty()) ||
(mStartDelayed && mConfig.showSnapshotDelay)) {
// Display the static image instead.
mStaticImage.setOpacity((unsigned char)(mFadeIn * 255.0f));
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;
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");
if (!elem)
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"))
setResize(elem->get<Vector2f>("size") * scale);
else if (elem->has("maxSize"))
@ -223,17 +233,15 @@ std::vector<HelpPrompt> VideoComponent::getHelpPrompts()
void VideoComponent::handleStartDelay()
{
// Only play if any delay has timed out
if (mStartDelayed)
{
if (mStartTime > SDL_GetTicks())
{
// Timeout not yet completed
// Only play if any delay has timed out.
if (mStartDelayed) {
if (mStartTime > SDL_GetTicks()) {
// Timeout not yet completed.
return;
}
// Completed
// Completed.
mStartDelayed = false;
// Clear the playing flag so startVideo works
// Clear the playing flag so startVideo works.
mIsPlaying = false;
startVideo();
}
@ -245,21 +253,18 @@ void VideoComponent::handleLooping()
void VideoComponent::startVideoWithDelay()
{
// If not playing then either start the video or initiate the delay
if (!mIsPlaying)
{
// Set the video that we are going to be playing so we don't attempt to restart it
// If not playing then either start the video or initiate the delay.
if (!mIsPlaying) {
// Set the video that we are going to be playing so we don't attempt to restart it.
mPlayingVideoPath = mVideoPath;
if (mConfig.startDelay == 0 || PowerSaver::getMode() == PowerSaver::INSTANT)
{
// No delay. Just start the video
if (mConfig.startDelay == 0 || PowerSaver::getMode() == PowerSaver::INSTANT) {
// No delay. Just start the video.
mStartDelayed = false;
startVideo();
}
else
{
// Configure the start delay
else {
// Configure the start delay.
mStartDelayed = true;
mFadeIn = 0.0f;
mStartTime = SDL_GetTicks() + mConfig.startDelay;
@ -272,24 +277,20 @@ void VideoComponent::update(int deltaTime)
{
manageState();
// If the video start is delayed and there is less than the fade time then set the image fade
// accordingly
if (mStartDelayed)
{
// If the video start is delayed and there is less than the fade time, then set
// the image fade accordingly.
if (mStartDelayed) {
Uint32 ticks = SDL_GetTicks();
if (mStartTime > ticks)
{
if (mStartTime > ticks) {
Uint32 diff = mStartTime - ticks;
if (diff < FADE_TIME_MS)
{
if (diff < FADE_TIME_MS) {
mFadeIn = (float)diff / (float)FADE_TIME_MS;
return;
}
}
}
// If the fade in is less than 1 then increment it
if (mFadeIn < 1.0f)
{
// If the fade in is less than 1 then increment it.
if (mFadeIn < 1.0f) {
mFadeIn += deltaTime / (float)FADE_TIME_MS;
if (mFadeIn > 1.0f)
mFadeIn = 1.0f;
@ -300,37 +301,30 @@ void VideoComponent::update(int deltaTime)
void VideoComponent::manageState()
{
// We will only show if the component is on display and the screensaver
// is not active
// is not active.
bool show = mShowing && !mScreensaverActive && !mDisable;
// See if we're already playing
if (mIsPlaying)
{
// If we are not on display then stop the video from playing
if (!show)
{
// See if we're already playing.
if (mIsPlaying) {
// If we are not on display then stop the video from playing.
if (!show) {
stopVideo();
}
else
{
if (mVideoPath != mPlayingVideoPath)
{
else {
if (mVideoPath != mPlayingVideoPath) {
// Path changed. Stop the video. We will start it again below because
// mIsPlaying will be modified by stopVideo to be false
// 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
// 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())
{
startVideoWithDelay();
}
}
}
void VideoComponent::onShow()
{

View file

@ -1,9 +1,16 @@
//
// VideoComponent.h
//
// Base class for playing videos.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
#define ES_CORE_COMPONENTS_VIDEO_COMPONENT_H
#include "components/ImageComponent.h"
#include "GuiComponent.h"
#include <string>
class TextureResource;
@ -14,9 +21,8 @@ void writeSubtitle(const char* gameName, const char* systemName, bool always);
class VideoComponent : public GuiComponent
{
// Structure that groups together the configuration of the video component
struct Configuration
{
// Structure that groups together the configuration of the video component.
struct Configuration {
unsigned startDelay;
bool showSnapshotNoVideo;
bool showSnapshotDelay;
@ -27,15 +33,15 @@ public:
VideoComponent(Window* window);
virtual ~VideoComponent();
// Loads the video at the given filepath
// Loads the video at the given filepath.
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);
// Configures the component to show the default video
// Configures the component to show the default video.
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);
virtual void onShow() override;
@ -52,40 +58,41 @@ public:
void render(const Transform4x4f& parentTrans) override;
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 void update(int deltaTime) 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.
// Can be set before or after a video is loaded.
// 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.
virtual void setResize(float width, float height) = 0;
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.
// 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.
virtual void setMaxSize(float width, float height) = 0;
inline void setMaxSize(const Vector2f& size) { setMaxSize(size.x(), size.y()); }
private:
// Start the video Immediately
// Start the video immediately.
virtual void startVideo() = 0;
// Stop the video
// Stop the video.
virtual void stopVideo() { };
// Handle looping the video. Must be called periodically
// Handle looping the video. Must be called periodically.
virtual void handleLooping();
// Start the video after any configured delay
// Start the video after any configured delay.
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();
// Manage the playing state of the component
// Manage the playing state of the component.
void manageState();
protected:

View file

@ -1,3 +1,9 @@
//
// VideoPlayerComponent.cpp
//
// OMXPlayer video playing for Raspberry Pi.
//
#ifdef _RPI_
#include "components/VideoPlayerComponent.h"
@ -5,6 +11,7 @@
#include "utils/StringUtil.h"
#include "AudioManager.h"
#include "Settings.h"
#include <fcntl.h>
#include <unistd.h>
#include <wait.h>
@ -59,55 +66,46 @@ void VideoPlayerComponent::setMaxSize(float width, float height)
void VideoPlayerComponent::startVideo()
{
if (!mIsPlaying)
{
if (!mIsPlaying) {
mVideoWidth = 0;
mVideoHeight = 0;
std::string path(mVideoPath.c_str());
// Make sure we have a video path
if ((path.size() > 0) && (mPlayerPid == -1))
{
// Set the video that we are going to be playing so we don't attempt to restart it
// Make sure we have a video path.
if ((path.size() > 0) && (mPlayerPid == -1)) {
// Set the video that we are going to be playing so we don't attempt to restart it.
mPlayingVideoPath = mVideoPath;
// Disable AudioManager so video can play, in case we're requesting ALSA
if (Utils::String::startsWith(Settings::getInstance()->getString("OMXAudioDev").c_str(), "alsa"))
{
// Disable AudioManager so video can play, in case we're requesting ALSA.
if (Utils::String::startsWith(Settings::getInstance()->
getString("OMXAudioDev").c_str(), "alsa"))
AudioManager::getInstance()->deinit();
}
// Start the player process
// Start the player process.
pid_t pid = fork();
if (pid == -1)
{
// Failed
if (pid == -1) {
// Failed.
mPlayingVideoPath = "";
}
else if (pid > 0)
{
else if (pid > 0) {
mPlayerPid = pid;
// Update the playing state
// Update the playing state.
signal(SIGCHLD, catch_child);
mIsPlaying = true;
mFadeIn = 0.0f;
}
else
{
else {
// Find out the pixel position of the video view and build a command line for
// omxplayer to position it in the right place
// 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());
// fix x and y
switch(Renderer::getScreenRotate())
{
case 0:
{
// Fix x and y.
switch (Renderer::getScreenRotate()) {
case 0: {
const int x1 = (int)(Renderer::getScreenOffsetX() + x);
const int y1 = (int)(Renderer::getScreenOffsetY() + y);
const int x2 = (int)(x1 + mSize.x());
@ -116,9 +114,9 @@ void VideoPlayerComponent::startVideo()
}
break;
case 1:
{
const int x1 = (int)(Renderer::getWindowWidth() - Renderer::getScreenOffsetY() - y - mSize.y());
case 1: {
const int x1 = (int)(Renderer::getWindowWidth() -
Renderer::getScreenOffsetY() - y - mSize.y());
const int y1 = (int)(Renderer::getScreenOffsetX() + x);
const int x2 = (int)(x1 + mSize.y());
const int y2 = (int)(y1 + mSize.x());
@ -126,20 +124,21 @@ void VideoPlayerComponent::startVideo()
}
break;
case 2:
{
const int x1 = (int)(Renderer::getWindowWidth() - Renderer::getScreenOffsetX() - x - mSize.x());
const int y1 = (int)(Renderer::getWindowHeight() - Renderer::getScreenOffsetY() - y - mSize.y());
case 2: {
const int x1 = (int)(Renderer::getWindowWidth() -
Renderer::getScreenOffsetX() - x - mSize.x());
const int y1 = (int)(Renderer::getWindowHeight() -
Renderer::getScreenOffsetY() - 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;
case 3:
{
case 3: {
const int x1 = (int)(Renderer::getScreenOffsetY() + y);
const int y1 = (int)(Renderer::getWindowHeight() - Renderer::getScreenOffsetX() - x - mSize.x());
const int y1 = (int)(Renderer::getWindowHeight() -
Renderer::getScreenOffsetX() - x - mSize.x());
const int x2 = (int)(x1 + mSize.y());
const int y2 = (int)(y1 + mSize.x());
sprintf(buf1, "%d,%d,%d,%d", x1, y1, x2, y2);
@ -147,72 +146,67 @@ void VideoPlayerComponent::startVideo()
break;
}
// rotate the video
switch(Renderer::getScreenRotate())
{
// Rotate the video.
switch (Renderer::getScreenRotate()) {
case 0: { sprintf(buf2, "%d", (int) 0); } break;
case 1: { sprintf(buf2, "%d", (int) 90); } break;
case 2: { sprintf(buf2, "%d", (int)180); } break;
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
// of our SDL display
// We need to specify the layer of 10000 or above to ensure the video is
// displayed on top of our SDL display.
const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd",
"--aspect-mode", "letterbox", "--vol", "0", "-o", "both",
"--win", buf1, "--orientation", buf2, "", "", "", "", "", "",
"", "", "", "", "", NULL };
const char* argv[] = { "", "--layer", "10010", "--loop", "--no-osd", "--aspect-mode", "letterbox", "--vol", "0", "-o", "both","--win", buf1, "--orientation", buf2, "", "", "", "", "", "", "", "", "", "", "", NULL };
// check if we want to mute the audio
if ((!Settings::getInstance()->getBool("VideoAudio") || (float)VolumeControl::getInstance()->getVolume() == 0) ||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
{
// Check if we want to mute the audio.
if ((!Settings::getInstance()->getBool("VideoAudio") ||
(float)VolumeControl::getInstance()->getVolume() == 0) ||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") &&
mScreensaverMode)) {
argv[8] = "-1000000";
}
else
{
else {
float percentVolume = (float)VolumeControl::getInstance()->getVolume();
int OMXVolume = (int)((percentVolume-98)*105);
argv[8] = std::to_string(OMXVolume).c_str();
}
// 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
// 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";
}
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never")
{
// if we have chosen to render subtitles
if (Settings::getInstance()->getString("ScreenSaverGameInfo") != "never") {
// If we have chosen to render subtitles.
argv[15] = "--subtitles";
argv[16] = subtitlePath.c_str();
argv[17] = mPlayingVideoPath.c_str();
argv[18] = "--font";
argv[19] = Settings::getInstance()->getString("SubtitleFont").c_str();
argv[20] = "--italic-font";
argv[21] = Settings::getInstance()->getString("SubtitleItalicFont").c_str();
argv[21] = Settings::getInstance()->
getString("SubtitleItalicFont").c_str();
argv[22] = "--font-size";
argv[23] = std::to_string(Settings::getInstance()->getInt("SubtitleSize")).c_str();
argv[23] = std::to_string(Settings::getInstance()->
getInt("SubtitleSize")).c_str();
argv[24] = "--align";
argv[25] = Settings::getInstance()->getString("SubtitleAlignment").c_str();
argv[25] = Settings::getInstance()->
getString("SubtitleAlignment").c_str();
}
else
{
// if we have chosen NOT to render subtitles in the screensaver
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
else {
// If we are rendering a video gamelist.
if (!mTargetIsMax)
{
argv[6] = "stretch";
}
argv[15] = mPlayingVideoPath.c_str();
}
@ -221,12 +215,12 @@ void VideoPlayerComponent::startVideo()
//const char* argv[] = args;
const char* env[] = { "LD_LIBRARY_PATH=/opt/vc/libs:/usr/lib/omxplayer", NULL };
// Redirect stdout
// 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
// Run the OMXPlayer binary.
execve("/usr/bin/omxplayer.bin", (char**)argv, (char**)env);
_exit(EXIT_FAILURE);
@ -237,7 +231,7 @@ void VideoPlayerComponent::startVideo()
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;
wait(&child_status);
}
@ -247,9 +241,8 @@ void VideoPlayerComponent::stopVideo()
mIsPlaying = false;
mStartDelayed = false;
// Stop the player process
if (mPlayerPid != -1)
{
// Stop the player process.
if (mPlayerPid != -1) {
int status;
kill(mPlayerPid, SIGKILL);
waitpid(mPlayerPid, &status, WNOHANG);
@ -258,4 +251,3 @@ void VideoPlayerComponent::stopVideo()
}
#endif

View file

@ -1,3 +1,9 @@
//
// VideoPlayerComponent.h
//
// OMXPlayer video playing for Raspberry Pi.
//
#ifdef _RPI_
#pragma once
#ifndef ES_CORE_COMPONENTS_VIDEO_PLAYER_COMPONENT_H
@ -15,22 +21,22 @@ public:
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.
// Can be set before or after a video is loaded.
// 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);
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.
// This 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);
void setMaxSize(float width, float height) override;
private:
// Start the video Immediately
virtual void startVideo();
// Stop the video
virtual void stopVideo();
// Start the video Immediately.
virtual void startVideo() override;
// Stop the video.
virtual void stopVideo() override;
private:
pid_t mPlayerPid;

View file

@ -1,3 +1,9 @@
//
// VideoVlcComponent.cpp
//
// Video playing using libVLC.
//
#include "components/VideoVlcComponent.h"
#include "renderers/Renderer.h"
@ -20,7 +26,7 @@
#include <codecvt>
#endif
libvlc_instance_t* VideoVlcComponent::mVLC = NULL;
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
// VLC prepares to render a video frame.
static void* lock(void* data, void** p_pixels) {
@ -28,7 +34,7 @@ static void *lock(void *data, void **p_pixels) {
SDL_LockMutex(c->mutex);
SDL_LockSurface(c->surface);
*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.
@ -40,19 +46,18 @@ static void unlock(void *data, void* /*id*/, void *const* /*p_pixels*/) {
// VLC wants to display a video frame.
static void display(void* /*data*/, void* /*id*/) {
//Data to be displayed
// Data to be displayed.
}
VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles) :
VideoComponent(window),
mMediaPlayer(nullptr)
VideoVlcComponent::VideoVlcComponent(Window* window, std::string subtitles)
: VideoComponent(window), mMediaPlayer(nullptr)
{
memset(&mContext, 0, sizeof(mContext));
// Get an empty texture for rendering the video
// Get an empty texture for rendering the video.
mTexture = TextureResource::get("");
// Make sure VLC has been initialised
// Make sure VLC has been initialized.
setupVLC(subtitles);
}
@ -87,51 +92,50 @@ void VideoVlcComponent::resize()
if (textureSize == Vector2f::Zero())
return;
// SVG rasterization is determined by height (see SVGResource.cpp), 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
// so, we always make sure the resultant height is an integer to make sure cutoff doesn't 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)
{
// 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.
// So we always make sure the resultant height is an integer to make sure cutoff doesn't
// 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;
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
if(resizeScale.x() < resizeScale.y())
{
if (resizeScale.x() < resizeScale.y()) {
mSize[0] *= resizeScale.x();
mSize[1] *= resizeScale.x();
}else{
}
else {
mSize[0] *= resizeScale.y();
mSize[1] *= resizeScale.y();
}
// for SVG rasterization, always calculate width from rounded height (see comment above)
// 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();
}else{
// if both components are set, we just stretch
// if no components are set, we don't resize at all
}
else {
// 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())
{
// 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())
{
}
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
// mSize.y() should already be rounded.
mTexture->rasterizeAt((size_t)Math::round(mSize.x()), (size_t)Math::round(mSize.y()));
onSizeChanged();
@ -147,10 +151,10 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
GuiComponent::renderChildren(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 color = Renderer::convertColor((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
const unsigned int color =
Renderer::convertColor((fadeIn << 24) | (fadeIn << 16) | (fadeIn << 8) | 255);
Renderer::Vertex vertices[4];
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
@ -158,29 +162,29 @@ void VideoVlcComponent::render(const Transform4x4f& parentTrans)
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
// round vertices
// Round vertices.
for (int i = 0; i < 4; ++i)
vertices[i].pos.round();
// Build a texture for the video frame
mTexture->initFromPixels((unsigned char*)mContext.surface->pixels, mContext.surface->w, mContext.surface->h);
// Build a texture for the video frame.
mTexture->initFromPixels((unsigned char*)mContext.surface->pixels,
mContext.surface->w, mContext.surface->h);
mTexture->bind();
// Render it
// Render it.
Renderer::drawTriangleStrips(&vertices[0], 4);
}
else
{
else {
VideoComponent::renderSnapshot(parentTrans);
}
}
void VideoVlcComponent::setupContext()
{
if (!mContext.valid)
{
// Create an RGBA surface to render the video into
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth, (int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
if (!mContext.valid) {
// Create an RGBA surface to render the video into.
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, (int)mVideoWidth,
(int)mVideoHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
mContext.mutex = SDL_CreateMutex();
mContext.valid = true;
resize();
@ -189,8 +193,7 @@ void VideoVlcComponent::setupContext()
void VideoVlcComponent::freeContext()
{
if (mContext.valid)
{
if (mContext.valid) {
SDL_FreeSurface(mContext.surface);
SDL_DestroyMutex(mContext.mutex);
mContext.valid = false;
@ -199,21 +202,18 @@ void VideoVlcComponent::freeContext()
void VideoVlcComponent::setupVLC(std::string subtitles)
{
// If VLC hasn't been initialised yet then do it now
if (!mVLC)
{
// If VLC hasn't been initialised yet then do it now.
if (!mVLC) {
const char** args;
const char* newargs[] = { "--quiet", "--sub-file", subtitles.c_str() };
const char* singleargs[] = { "--quiet" };
int argslen = 0;
if (!subtitles.empty())
{
if (!subtitles.empty()) {
argslen = sizeof(newargs) / sizeof(newargs[0]);
args = newargs;
}
else
{
else {
argslen = sizeof(singleargs) / sizeof(singleargs[0]);
args = singleargs;
}
@ -223,16 +223,13 @@ void VideoVlcComponent::setupVLC(std::string subtitles)
void VideoVlcComponent::handleLooping()
{
if (mIsPlaying && mMediaPlayer)
{
if (mIsPlaying && 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))
{
libvlc_audio_set_mute(mMediaPlayer, 1);
}
//libvlc_media_player_set_position(mMediaPlayer, 0.0f);
libvlc_media_player_set_media(mMediaPlayer, mMedia);
libvlc_media_player_play(mMediaPlayer);
@ -251,30 +248,27 @@ void VideoVlcComponent::startVideo()
#else
std::string path(mVideoPath);
#endif
// Make sure we have a video path
if (mVLC && (path.size() > 0))
{
// Set the video that we are going to be playing so we don't attempt to restart it
// Make sure we have a video path.
if (mVLC && (path.size() > 0)) {
// Set the video that we are going to be playing so we don't attempt to restart it.
mPlayingVideoPath = mVideoPath;
// Open the media
// Open the media.
mMedia = libvlc_media_new_path(mVLC, path.c_str());
if (mMedia)
{
if (mMedia) {
unsigned track_count;
int parseResult;
libvlc_event_t vlcEvent;
// Asynchronous media parsing
libvlc_event_attach(libvlc_media_event_manager(mMedia), libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
// Asynchronous media parsing.
libvlc_event_attach(libvlc_media_event_manager(
mMedia), libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
if (!parseResult)
{
// Wait for a maximum of 1 second for the media parsing
for(int i=0; i<200; i++)
{
if ((libvlc_media_get_parsed_status(mMedia)))
if (!parseResult) {
// Wait for a maximum of 1 second for the media parsing.
for (int i = 0; i < 200; i++) {
if (libvlc_media_get_parsed_status(mMedia))
break;
SDL_Delay(5);
};
@ -282,10 +276,8 @@ void VideoVlcComponent::startVideo()
libvlc_media_track_t** tracks;
track_count = libvlc_media_tracks_get(mMedia, &tracks);
for (unsigned track = 0; track < track_count; ++track)
{
if (tracks[track]->i_type == libvlc_track_video)
{
for (unsigned track = 0; track < track_count; ++track) {
if (tracks[track]->i_type == libvlc_track_video) {
mVideoWidth = tracks[track]->video->i_width;
mVideoHeight = tracks[track]->video->i_height;
break;
@ -293,21 +285,20 @@ void VideoVlcComponent::startVideo()
}
libvlc_media_tracks_release(tracks, track_count);
// Make sure we found a valid video track
if ((mVideoWidth > 0) && (mVideoHeight > 0))
{
// Make sure we found a valid video track.
if ((mVideoWidth > 0) && (mVideoHeight > 0)) {
#ifndef _RPI_
if (mScreensaverMode)
{
if (mScreensaverMode) {
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());
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.x());
}else{
}
else {
mVideoWidth = (unsigned int) (mVideoWidth * resizeScale.y());
mVideoHeight = (unsigned int) (mVideoHeight * resizeScale.y());
}
@ -317,20 +308,21 @@ void VideoVlcComponent::startVideo()
PowerSaver::pause();
setupContext();
// Setup the media player
// Setup the media player.
mMediaPlayer = libvlc_media_player_new_from_media(mMedia);
if (!Settings::getInstance()->getBool("VideoAudio") ||
(Settings::getInstance()->getBool("ScreenSaverVideoMute") && mScreensaverMode))
{
(Settings::getInstance()->getBool("ScreenSaverVideoMute") &&
mScreensaverMode))
libvlc_audio_set_mute(mMediaPlayer, 1);
}
libvlc_media_player_play(mMediaPlayer);
libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display, (void*)&mContext);
libvlc_video_set_format(mMediaPlayer, "RGBA", (int)mVideoWidth, (int)mVideoHeight, (int)mVideoWidth * 4);
libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display,
(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;
mFadeIn = 0.0f;
}
@ -343,13 +335,12 @@ void VideoVlcComponent::stopVideo()
{
mIsPlaying = false;
mStartDelayed = false;
// Release the media player so it stops calling back to us
if (mMediaPlayer)
{
// Release the media player so it stops calling back to us.
if (mMediaPlayer) {
libvlc_media_player_stop(mMediaPlayer);
libvlc_media_player_release(mMediaPlayer);
libvlc_media_release(mMedia);
mMediaPlayer = NULL;
mMediaPlayer = nullptr;
freeContext();
PowerSaver::resume();
}

View file

@ -1,8 +1,15 @@
//
// VideoVlcComponent.h
//
// Video playing using libVLC.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
#define ES_CORE_COMPONENTS_VIDEO_VLC_COMPONENT_H
#include "VideoComponent.h"
#include <vlc/vlc.h>
struct SDL_mutex;
@ -19,9 +26,8 @@ struct VideoContext {
class VideoVlcComponent : public VideoComponent
{
// Structure that groups together the configuration of the video component
struct Configuration
{
// Structure that groups together the configuration of the video component.
struct Configuration {
unsigned startDelay;
bool showSnapshotNoVideo;
bool showSnapshotDelay;
@ -36,15 +42,14 @@ public:
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.
// Can be set before or after a video is loaded.
// 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 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.
void setMaxSize(float width, float height) override;
@ -52,11 +57,11 @@ private:
// Calculates the correct mSize from our resizing information (set by setResize/setMaxSize).
// Used internally whenever the resizing parameters or texture change.
void resize();
// Start the video Immediately
// Start the video immediately.
virtual void startVideo() override;
// Stop the video
// Stop the video.
virtual void stopVideo() override;
// Handle looping the video. Must be called periodically
// Handle looping the video. Must be called periodically.
virtual void handleLooping() override;
void setupContext();

View file

@ -1,7 +1,7 @@
//
// Renderer.cpp
//
// Rendering functions.
// General rendering functions.
//
#include "renderers/Renderer.h"

View file

@ -1,7 +1,7 @@
//
// Renderer.h
//
// Rendering functions.
// General rendering functions.
//
#pragma once

View file

@ -60,7 +60,7 @@ const ResourceData ResourceManager::getFileData(const std::string& path) const
}
// If the file doesn't exist, return an "empty" ResourceData.
ResourceData data = {NULL, 0};
ResourceData data = {nullptr, 0};
return data;
}

View file

@ -54,7 +54,7 @@ bool TextureData::initSVGFromMemory(const unsigned char* fileData, size_t length
// nsvgParse excepts a modifiable, null-terminated string.
char* copy = (char*)malloc(length + 1);
assert(copy != NULL);
assert(copy != nullptr);
memcpy(copy, fileData, length);
copy[length] = '\0';