Code cleanup and code documentation. Only cosmetic changes in this commit.

This commit is contained in:
Leon Styhre 2020-06-06 16:48:05 +02:00
parent b7feedd287
commit 709e6b996e
20 changed files with 944 additions and 841 deletions

View file

@ -7,7 +7,7 @@
// Improved and extended by the RetroPie community.
// Desktop Edition fork by Leon Styhre.
//
// The line length limit is 100 characters.
// The line length limit is 100 characters and the tab width is 4 spaces.
//
// main.cpp
//

View file

@ -1,3 +1,9 @@
//
// SystemView.cpp
//
// Main system view.
//
#include "views/SystemView.h"
#include "animations/LambdaAnimation.h"
@ -10,13 +16,16 @@
#include "Window.h"
#include "Sound.h"
// buffer values for scrolling velocity (left, stopped, right)
// Buffer values for scrolling velocity (left, stopped, right).
const int logoBuffersLeft[] = { -5, -2, -1 };
const int logoBuffersRight[] = { 1, 2, 5 };
SystemView::SystemView(Window* window) : IList<SystemViewData, SystemData*>(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP),
mViewNeedsReload(true),
mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
SystemView::SystemView(
Window* window)
: IList<SystemViewData, SystemData*>
(window, LIST_SCROLL_STYLE_SLOW, LIST_ALWAYS_LOOP),
mViewNeedsReload(true),
mSystemInfo(window, "SYSTEM INFO", Font::get(FONT_SIZE_SMALL), 0x33333300, ALIGN_CENTER)
{
mCamOffset = 0;
mExtrasCamOffset = 0;
@ -30,28 +39,27 @@ void SystemView::populate()
{
mEntries.clear();
for(auto it = SystemData::sSystemVector.cbegin(); it != SystemData::sSystemVector.cend(); it++)
{
for (auto it = SystemData::sSystemVector.cbegin();
it != SystemData::sSystemVector.cend(); it++) {
const std::shared_ptr<ThemeData>& theme = (*it)->getTheme();
if(mViewNeedsReload)
if (mViewNeedsReload)
getViewElements(theme);
if((*it)->isVisible())
{
if ((*it)->isVisible()) {
Entry e;
e.name = (*it)->getName();
e.object = *it;
// make logo
// Make logo.
const ThemeData::ThemeElement* logoElem = theme->getElement("system", "logo", "image");
if(logoElem)
{
if (logoElem) {
std::string path = logoElem->get<std::string>("path");
std::string defaultPath = logoElem->has("default") ? logoElem->get<std::string>("default") : "";
if((!path.empty() && ResourceManager::getInstance()->fileExists(path))
|| (!defaultPath.empty() && ResourceManager::getInstance()->fileExists(defaultPath)))
{
std::string defaultPath = logoElem->has("default") ?
logoElem->get<std::string>("default") : "";
if ((!path.empty() && ResourceManager::getInstance()->fileExists(path)) ||
(!defaultPath.empty() &&
ResourceManager::getInstance()->fileExists(defaultPath))) {
ImageComponent* logo = new ImageComponent(mWindow, false, false);
logo->setMaxSize(mCarousel.logoSize * mCarousel.logoScale);
logo->applyTheme(theme, "system", "logo", ThemeFlags::PATH | ThemeFlags::COLOR);
@ -59,37 +67,39 @@ void SystemView::populate()
e.data.logo = std::shared_ptr<GuiComponent>(logo);
}
}
if (!e.data.logo)
{
// no logo in theme; use text
TextComponent* text = new TextComponent(mWindow,
if (!e.data.logo) {
// No logo in theme; use text.
TextComponent* text = new TextComponent(
mWindow,
(*it)->getName(),
Font::get(FONT_SIZE_LARGE),
0x000000FF,
ALIGN_CENTER);
text->setSize(mCarousel.logoSize * mCarousel.logoScale);
text->applyTheme((*it)->getTheme(), "system", "logoText", ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR | ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT);
text->applyTheme((*it)->getTheme(), "system", "logoText",
ThemeFlags::FONT_PATH | ThemeFlags::FONT_SIZE | ThemeFlags::COLOR |
ThemeFlags::FORCE_UPPERCASE | ThemeFlags::LINE_SPACING | ThemeFlags::TEXT);
e.data.logo = std::shared_ptr<GuiComponent>(text);
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL)
{
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) {
text->setHorizontalAlignment(mCarousel.logoAlignment);
text->setVerticalAlignment(ALIGN_CENTER);
} else {
}
else {
text->setHorizontalAlignment(ALIGN_CENTER);
text->setVerticalAlignment(mCarousel.logoAlignment);
}
}
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL)
{
if (mCarousel.type == VERTICAL || mCarousel.type == VERTICAL_WHEEL) {
if (mCarousel.logoAlignment == ALIGN_LEFT)
e.data.logo->setOrigin(0, 0.5);
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
e.data.logo->setOrigin(1.0, 0.5);
else
e.data.logo->setOrigin(0.5, 0.5);
} else {
}
else {
if (mCarousel.logoAlignment == ALIGN_TOP)
e.data.logo->setOrigin(0.5, 0);
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
@ -100,29 +110,30 @@ void SystemView::populate()
Vector2f denormalized = mCarousel.logoSize * e.data.logo->getOrigin();
e.data.logo->setPosition(denormalized.x(), denormalized.y(), 0.0);
// delete any existing extras
// Delete any existing extras.
for (auto extra : e.data.backgroundExtras)
delete extra;
e.data.backgroundExtras.clear();
// make background extras
// Make background extras.
e.data.backgroundExtras = ThemeData::makeExtras((*it)->getTheme(), "system", mWindow);
// sort the extras by z-index
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(), [](GuiComponent* a, GuiComponent* b) {
// Sort the extras by z-index.
std::stable_sort(e.data.backgroundExtras.begin(), e.data.backgroundExtras.end(),
[](GuiComponent* a, GuiComponent* b) {
return b->getZIndex() > a->getZIndex();
});
this->add(e);
}
}
if (mEntries.size() == 0)
{
// Something is wrong, there is not a single system to show, check if UI mode is not full
if (!UIModeController::getInstance()->isUIModeFull())
{
if (mEntries.size() == 0) {
// Something is wrong, there is not a single system to show, check if UI mode is not full.
if (!UIModeController::getInstance()->isUIModeFull()) {
Settings::getInstance()->setString("UIMode", "Full");
mWindow->pushGui(new GuiMsgBox(mWindow, "The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK", nullptr));
mWindow->pushGui(new GuiMsgBox(mWindow,
"The selected UI mode has nothing to show,\n returning to UI mode: FULL", "OK",
nullptr));
}
}
}
@ -131,7 +142,7 @@ void SystemView::goToSystem(SystemData* system, bool animate)
{
setCursor(system);
if(!animate)
if (!animate)
finishAnimation(0);
}
@ -140,27 +151,23 @@ bool SystemView::input(InputConfig* config, Input input)
auto it = SystemData::sSystemVector.cbegin();
const std::shared_ptr<ThemeData>& theme = (*it)->getTheme();
if(input.value != 0)
{
if(config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r && SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug"))
{
if (input.value != 0) {
if (config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_r &&
SDL_GetModState() & KMOD_LCTRL && Settings::getInstance()->getBool("Debug")) {
LOG(LogInfo) << " Reloading all";
ViewController::get()->reloadAll();
return true;
}
switch (mCarousel.type)
{
switch (mCarousel.type) {
case VERTICAL:
case VERTICAL_WHEEL:
if (config->isMappedLike("up", input))
{
if (config->isMappedLike("up", input)) {
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
listInput(-1);
return true;
}
if (config->isMappedLike("down", input))
{
if (config->isMappedLike("down", input)) {
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
listInput(1);
return true;
@ -169,14 +176,12 @@ bool SystemView::input(InputConfig* config, Input input)
case HORIZONTAL:
case HORIZONTAL_WHEEL:
default:
if (config->isMappedLike("left", input))
{
if (config->isMappedLike("left", input)) {
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
listInput(-1);
return true;
}
if (config->isMappedLike("right", input))
{
if (config->isMappedLike("right", input)) {
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
listInput(1);
return true;
@ -184,29 +189,29 @@ bool SystemView::input(InputConfig* config, Input input)
break;
}
if(config->isMappedTo("a", input))
{
if (config->isMappedTo("a", input)) {
stopScrolling();
ViewController::get()->goToGameList(getSelected());
NavigationSounds::getInstance()->playThemeNavigationSound(SELECTSOUND);
return true;
}
if (config->isMappedTo("x", input))
{
// get random system
// go to system
if (config->isMappedTo("x", input)) {
// Get random system.
// Go to system.
NavigationSounds::getInstance()->playThemeNavigationSound(SYSTEMBROWSESOUND);
setCursor(SystemData::getRandomSystem());
return true;
}
}else{
if(config->isMappedLike("left", input) ||
}
else {
if (config->isMappedLike("left", input) ||
config->isMappedLike("right", input) ||
config->isMappedLike("up", input) ||
config->isMappedLike("down", input))
listInput(0);
if(!UIModeController::getInstance()->isUIModeKid() && config->isMappedTo("select", input) && Settings::getInstance()->getBool("ScreenSaverControls"))
{
if (!UIModeController::getInstance()->isUIModeKid() &&
config->isMappedTo("select", input) &&
Settings::getInstance()->getBool("ScreenSaverControls")) {
mWindow->startScreenSaver();
mWindow->renderScreenSaver();
return true;
@ -224,7 +229,7 @@ void SystemView::update(int deltaTime)
void SystemView::onCursorChanged(const CursorState& /*state*/)
{
// update help style
// Update help style.
updateHelpPrompts();
float startPos = mCamOffset;
@ -232,18 +237,18 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
float posMax = (float)mEntries.size();
float target = (float)mCursor;
// what's the shortest way to get to our target?
// it's one of these...
// What's the shortest way to get to our target?
// It's one of these...
float endPos = target; // directly
float endPos = target; // Directly.
float dist = abs(endPos - startPos);
if(abs(target + posMax - startPos) < dist)
endPos = target + posMax; // loop around the end (0 -> max)
if(abs(target - posMax - startPos) < dist)
endPos = target - posMax; // loop around the start (max - 1 -> -1)
if (abs(target + posMax - startPos) < dist)
endPos = target + posMax; // Loop around the end (0 -> max).
if (abs(target - posMax - startPos) < dist)
endPos = target - posMax; // Loop around the start (max - 1 -> -1).
// animate mSystemInfo's opacity (fade out, wait, fade back in)
// Animate mSystemInfo's opacity (fade out, wait, fade back in).
cancelAnimation(1);
cancelAnimation(2);
@ -253,14 +258,13 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
const float infoStartOpacity = mSystemInfo.getOpacity() / 255.f;
Animation* infoFadeOut = new LambdaAnimation(
[infoStartOpacity, this] (float t)
{
[infoStartOpacity, this] (float t) {
mSystemInfo.setOpacity((unsigned char)(Math::lerp(infoStartOpacity, 0.f, t) * 255));
}, (int)(infoStartOpacity * (goFast ? 10 : 150)));
unsigned int gameCount = getSelected()->getDisplayedGameCount();
// also change the text after we've fully faded out
// Also change the text after we've fully faded out.
setAnimation(infoFadeOut, 0, [this, gameCount] {
std::stringstream ss;
@ -273,72 +277,69 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
}, false, 1);
Animation* infoFadeIn = new LambdaAnimation(
[this](float t)
{
[this](float t) {
mSystemInfo.setOpacity((unsigned char)(Math::lerp(0.f, 1.f, t) * 255));
}, goFast ? 10 : 300);
// wait 600ms to fade in
// Wait 600ms to fade in.
setAnimation(infoFadeIn, goFast ? 0 : 2000, nullptr, false, 2);
// no need to animate transition, we're not going anywhere (probably mEntries.size() == 1)
if(endPos == mCamOffset && endPos == mExtrasCamOffset)
// No need to animate transition, we're not going anywhere (probably mEntries.size() == 1).
if (endPos == mCamOffset && endPos == mExtrasCamOffset)
return;
Animation* anim;
bool move_carousel = Settings::getInstance()->getBool("MoveCarousel");
if(transition_style == "fade")
{
if (transition_style == "fade") {
float startExtrasFade = mExtrasFadeOpacity;
anim = new LambdaAnimation(
[this, startExtrasFade, startPos, endPos, posMax, move_carousel](float t)
{
[this, startExtrasFade, startPos, endPos, posMax, move_carousel](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1);
if(f < 0)
if (f < 0)
f += posMax;
if(f >= posMax)
if (f >= posMax)
f -= posMax;
this->mCamOffset = move_carousel ? f : endPos;
t += 1;
if(t < 0.3f)
if (t < 0.3f)
this->mExtrasFadeOpacity = Math::lerp(0.0f, 1.0f, t / 0.3f + startExtrasFade);
else if(t < 0.7f)
else if (t < 0.7f)
this->mExtrasFadeOpacity = 1.0f;
else
this->mExtrasFadeOpacity = Math::lerp(1.0f, 0.0f, (t - 0.7f) / 0.3f);
if(t > 0.5f)
if (t > 0.5f)
this->mExtrasCamOffset = endPos;
}, 500);
} else if (transition_style == "slide") {
// slide
}
else if (transition_style == "slide") {
// Slide.
anim = new LambdaAnimation(
[this, startPos, endPos, posMax, move_carousel](float t)
{
[this, startPos, endPos, posMax, move_carousel](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1);
if(f < 0)
if (f < 0)
f += posMax;
if(f >= posMax)
if (f >= posMax)
f -= posMax;
this->mCamOffset = move_carousel ? f : endPos;
this->mExtrasCamOffset = f;
}, 500);
} else {
// instant
}
else {
// Instant.
anim = new LambdaAnimation(
[this, startPos, endPos, posMax, move_carousel ](float t)
{
[this, startPos, endPos, posMax, move_carousel ](float t) {
t -= 1;
float f = Math::lerp(startPos, endPos, t*t*t + 1);
if(f < 0)
if (f < 0)
f += posMax;
if(f >= posMax)
if (f >= posMax)
f -= posMax;
this->mCamOffset = move_carousel ? f : endPos;
@ -346,14 +347,13 @@ void SystemView::onCursorChanged(const CursorState& /*state*/)
}, move_carousel ? 500 : 1);
}
setAnimation(anim, 0, nullptr, false, 0);
}
void SystemView::render(const Transform4x4f& parentTrans)
{
if(size() == 0)
return; // nothing to render
if (size() == 0)
return; // Nothing to render.
Transform4x4f trans = getTransform() * parentTrans;
@ -365,7 +365,8 @@ void SystemView::render(const Transform4x4f& parentTrans)
if (mCarousel.zIndex > mSystemInfo.getZIndex()) {
renderInfoBar(trans);
} else {
}
else {
renderCarousel(trans);
}
@ -373,7 +374,8 @@ void SystemView::render(const Transform4x4f& parentTrans)
if (mCarousel.zIndex > mSystemInfo.getZIndex()) {
renderCarousel(trans);
} else {
}
else {
renderInfoBar(trans);
}
@ -390,7 +392,8 @@ std::vector<HelpPrompt> SystemView::getHelpPrompts()
prompts.push_back(HelpPrompt("a", "select"));
prompts.push_back(HelpPrompt("x", "random"));
if (!UIModeController::getInstance()->isUIModeKid() && Settings::getInstance()->getBool("ScreenSaverControls"))
if (!UIModeController::getInstance()->isUIModeKid() &&
Settings::getInstance()->getBool("ScreenSaverControls"))
prompts.push_back(HelpPrompt("select", "launch screensaver"));
return prompts;
@ -420,40 +423,46 @@ void SystemView::getViewElements(const std::shared_ptr<ThemeData>& theme)
if (!theme->hasView("system"))
return;
const ThemeData::ThemeElement* carouselElem = theme->getElement("system", "systemcarousel", "carousel");
const ThemeData::ThemeElement* carouselElem = theme->
getElement("system", "systemcarousel", "carousel");
if (carouselElem)
getCarouselFromTheme(carouselElem);
const ThemeData::ThemeElement* sysInfoElem = theme->getElement("system", "systemInfo", "text");
const ThemeData::ThemeElement* sysInfoElem = theme->
getElement("system", "systemInfo", "text");
if (sysInfoElem)
mSystemInfo.applyTheme(theme, "system", "systemInfo", ThemeFlags::ALL);
mViewNeedsReload = false;
}
// Render system carousel
// Render system carousel.
void SystemView::renderCarousel(const Transform4x4f& trans)
{
// background box behind logos
// Background box behind logos.
Transform4x4f carouselTrans = trans;
carouselTrans.translate(Vector3f(mCarousel.pos.x(), mCarousel.pos.y(), 0.0));
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1, mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f));
carouselTrans.translate(Vector3f(mCarousel.origin.x() * mCarousel.size.x() * -1,
mCarousel.origin.y() * mCarousel.size.y() * -1, 0.0f));
Vector2f clipPos(carouselTrans.translation().x(), carouselTrans.translation().y());
Renderer::pushClipRect(Vector2i((int)clipPos.x(), (int)clipPos.y()), Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y()));
Renderer::pushClipRect(Vector2i((int)clipPos.x(), (int)clipPos.y()),
Vector2i((int)mCarousel.size.x(), (int)mCarousel.size.y()));
Renderer::setMatrix(carouselTrans);
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(), mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
Renderer::drawRect(0.0f, 0.0f, mCarousel.size.x(), mCarousel.size.y(),
mCarousel.color, mCarousel.colorEnd, mCarousel.colorGradientHorizontal);
// draw logos
Vector2f logoSpacing(0.0, 0.0); // NB: logoSpacing will include the size of the logo itself as well!
// Draw logos.
// NB: logoSpacing will also include the size of the logo itself!
Vector2f logoSpacing(0.0, 0.0);
float xOff = 0.0;
float yOff = 0.0;
switch (mCarousel.type)
{
switch (mCarousel.type) {
case VERTICAL_WHEEL:
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]);
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f -
(mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.f;
else if (mCarousel.logoAlignment == ALIGN_RIGHT)
@ -462,8 +471,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f;
break;
case VERTICAL:
logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y();
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f - (mCamOffset * logoSpacing[1]);
logoSpacing[1] = ((mCarousel.size.y() - (mCarousel.logoSize.y() *
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.y();
yOff = (mCarousel.size.y() - mCarousel.logoSize.y()) / 2.f -
(mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_LEFT)
xOff = mCarousel.logoSize.x() / 10.f;
@ -473,7 +484,8 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2;
break;
case HORIZONTAL_WHEEL:
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 - (mCamOffset * logoSpacing[1]);
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2 -
(mCamOffset * logoSpacing[1]);
if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10;
else if (mCarousel.logoAlignment == ALIGN_BOTTOM)
@ -483,8 +495,10 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
break;
case HORIZONTAL:
default:
logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() * mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x();
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f - (mCamOffset * logoSpacing[0]);
logoSpacing[0] = ((mCarousel.size.x() - (mCarousel.logoSize.x() *
mCarousel.maxLogoCount)) / (mCarousel.maxLogoCount)) + mCarousel.logoSize.x();
xOff = (mCarousel.size.x() - mCarousel.logoSize.x()) / 2.f -
(mCamOffset * logoSpacing[0]);
if (mCarousel.logoAlignment == ALIGN_TOP)
yOff = mCarousel.logoSize.y() / 10.f;
@ -498,18 +512,17 @@ void SystemView::renderCarousel(const Transform4x4f& trans)
int center = (int)(mCamOffset);
int logoCount = Math::min(mCarousel.maxLogoCount, (int)mEntries.size());
// Adding texture loading buffers depending on scrolling speed and status
// Adding texture loading buffers depending on scrolling speed and status.
int bufferIndex = getScrollingVelocity() + 1;
int bufferLeft = logoBuffersLeft[bufferIndex];
int bufferRight = logoBuffersRight[bufferIndex];
if (logoCount == 1)
{
if (logoCount == 1) {
bufferLeft = 0;
bufferRight = 0;
}
for (int i = center - logoCount / 2 + bufferLeft; i <= center + logoCount / 2 + bufferRight; i++)
{
for (int i = center - logoCount / 2 + bufferLeft;
i <= center + logoCount / 2 + bufferRight; i++) {
int index = i;
while (index < 0)
index += (int)mEntries.size();
@ -546,25 +559,25 @@ void SystemView::renderInfoBar(const Transform4x4f& trans)
mSystemInfo.render(trans);
}
// Draw background extras
// Draw background extras.
void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upper)
{
int extrasCenter = (int)mExtrasCamOffset;
// Adding texture loading buffers depending on scrolling speed and status
// Adding texture loading buffers depending on scrolling speed and status.
int bufferIndex = getScrollingVelocity() + 1;
Renderer::pushClipRect(Vector2i::Zero(), Vector2i((int)mSize.x(), (int)mSize.y()));
for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter + logoBuffersRight[bufferIndex]; i++)
{
for (int i = extrasCenter + logoBuffersLeft[bufferIndex]; i <= extrasCenter +
logoBuffersRight[bufferIndex]; i++) {
int index = i;
while (index < 0)
index += (int)mEntries.size();
while (index >= (int)mEntries.size())
index -= (int)mEntries.size();
//Only render selected system when not showing
// Only render selected system when not showing.
if (mShowing || index == mCursor)
{
Transform4x4f extrasTrans = trans;
@ -573,8 +586,9 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
else
extrasTrans.translate(Vector3f(0, (i - mExtrasCamOffset) * mSize.y(), 0));
Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0], (int)extrasTrans.translation()[1]),
Vector2i((int)mSize.x(), (int)mSize.y()));
Renderer::pushClipRect(Vector2i((int)extrasTrans.translation()[0],
(int)extrasTrans.translation()[1]),
Vector2i((int)mSize.x(), (int)mSize.y()));
SystemViewData data = mEntries.at(index).data;
for (unsigned int j = 0; j < data.backgroundExtras.size(); j++) {
GuiComponent *extra = data.backgroundExtras[j];
@ -590,19 +604,18 @@ void SystemView::renderExtras(const Transform4x4f& trans, float lower, float upp
void SystemView::renderFade(const Transform4x4f& trans)
{
// fade extras if necessary
if (mExtrasFadeOpacity)
{
// Fade extras if necessary.
if (mExtrasFadeOpacity) {
unsigned int fadeColor = 0x00000000 | (unsigned char)(mExtrasFadeOpacity * 255);
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, 0.0f, mSize.x(), mSize.y(), fadeColor, fadeColor);
}
}
// Populate the system carousel with the legacy values
// Populate the system carousel with the legacy values.
void SystemView::getDefaultElements(void)
{
// Carousel
// Carousel.
mCarousel.type = HORIZONTAL;
mCarousel.logoAlignment = ALIGN_CENTER;
mCarousel.size.x() = mSize.x();
@ -623,7 +636,7 @@ void SystemView::getDefaultElements(void)
mCarousel.maxLogoCount = 3;
mCarousel.zIndex = 40;
// System Info Bar
// System Info Bar.
mSystemInfo.setSize(mSize.x(), mSystemInfo.getFont()->getLetterHeight()*2.2f);
mSystemInfo.setPosition(0, (mCarousel.pos.y() + mCarousel.size.y() - 0.2f));
mSystemInfo.setBackgroundColor(0xDDDDDDD8);
@ -636,8 +649,7 @@ void SystemView::getDefaultElements(void)
void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
{
if (elem->has("type"))
{
if (elem->has("type")) {
if (!(elem->get<std::string>("type").compare("vertical")))
mCarousel.type = VERTICAL;
else if (!(elem->get<std::string>("type").compare("vertical_wheel")))
@ -653,8 +665,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
mCarousel.pos = elem->get<Vector2f>("pos") * mSize;
if (elem->has("origin"))
mCarousel.origin = elem->get<Vector2f>("origin");
if (elem->has("color"))
{
if (elem->has("color")) {
mCarousel.color = elem->get<unsigned int>("color");
mCarousel.colorEnd = mCarousel.color;
}
@ -674,8 +685,7 @@ void SystemView::getCarouselFromTheme(const ThemeData::ThemeElement* elem)
mCarousel.logoRotation = elem->get<float>("logoRotation");
if (elem->has("logoRotationOrigin"))
mCarousel.logoRotationOrigin = elem->get<Vector2f>("logoRotationOrigin");
if (elem->has("logoAlignment"))
{
if (elem->has("logoAlignment")) {
if (!(elem->get<std::string>("logoAlignment").compare("left")))
mCarousel.logoAlignment = ALIGN_LEFT;
else if (!(elem->get<std::string>("logoAlignment").compare("right")))

View file

@ -1,3 +1,9 @@
//
// SystemView.h
//
// Main system view.
//
#pragma once
#ifndef ES_APP_VIEWS_SYSTEM_VIEW_H
#define ES_APP_VIEWS_SYSTEM_VIEW_H
@ -11,22 +17,19 @@
class AnimatedImageComponent;
class SystemData;
enum CarouselType : unsigned int
{
enum CarouselType : unsigned int {
HORIZONTAL = 0,
VERTICAL = 1,
VERTICAL_WHEEL = 2,
HORIZONTAL_WHEEL = 3
};
struct SystemViewData
{
struct SystemViewData {
std::shared_ptr<GuiComponent> logo;
std::vector<GuiComponent*> backgroundExtras;
};
struct SystemViewCarousel
{
struct SystemViewCarousel {
CarouselType type;
Vector2f pos;
Vector2f size;
@ -38,7 +41,7 @@ struct SystemViewCarousel
unsigned int color;
unsigned int colorEnd;
bool colorGradientHorizontal;
int maxLogoCount; // number of logos shown on the carousel
int maxLogoCount; // Number of logos shown on the carousel.
Vector2f logoSize;
float zIndex;
};
@ -79,7 +82,7 @@ private:
SystemViewCarousel mCarousel;
TextComponent mSystemInfo;
// unit is list index
// Unit is list index.
float mCamOffset;
float mExtrasCamOffset;
float mExtrasFadeOpacity;

View file

@ -1,3 +1,10 @@
//
// UIModeController.cpp
//
// Handling of application user interface modes (full, kiosk and kid).
// This includes switching the mode when the UI mode passkey was used.
//
#include "UIModeController.h"
#include "utils/StringUtil.h"
@ -25,8 +32,8 @@ UIModeController::UIModeController()
void UIModeController::monitorUIMode()
{
std::string uimode = Settings::getInstance()->getString("UIMode");
if (uimode != mCurrentUIMode) // UIMODE HAS CHANGED
{
// UI mode was changed.
if (uimode != mCurrentUIMode) {
mCurrentUIMode = uimode;
ViewController::get()->ReloadAndGoToStart();
}
@ -34,26 +41,18 @@ void UIModeController::monitorUIMode()
bool UIModeController::listen(InputConfig * config, Input input)
{
// Reads the current input to listen for the passkey
// sequence to unlock the UI mode. The progress is saved in mPassKeyCounter
// Reads the current input to listen for the passkey sequence to unlock
// the UI mode. The progress is saved in mPassKeyCounter.
if (Settings::getInstance()->getBool("Debug"))
{
logInput(config, input);
}
if ((Settings::getInstance()->getString("UIMode") == "Full") || !isValidInput(config, input))
{
return false; // Already unlocked, or invalid input, nothing to do here.
}
if (!inputIsMatch(config, input))
{
mPassKeyCounter = 0; // current input is incorrect, reset counter
}
mPassKeyCounter = 0; // Current input is incorrect, reset counter.
if (mPassKeyCounter == (int)mPassKeySequence.length())
{
if (mPassKeyCounter == (int)mPassKeySequence.length()) {
unlockUIMode();
return true;
}
@ -62,11 +61,9 @@ bool UIModeController::listen(InputConfig * config, Input input)
bool UIModeController::inputIsMatch(InputConfig * config, Input input)
{
for (auto valstring : mInputVals)
{
for (auto valstring : mInputVals) {
if (config->isMappedLike(valstring, input) &&
(mPassKeySequence[mPassKeyCounter] == valstring[0]))
{
(mPassKeySequence[mPassKeyCounter] == valstring[0])) {
mPassKeyCounter++;
return true;
}
@ -74,10 +71,11 @@ bool UIModeController::inputIsMatch(InputConfig * config, Input input)
return false;
}
// When we have reached the end of the list, trigger UI_mode unlock
// When we have reached the end of the list, trigger UI_mode unlock.
void UIModeController::unlockUIMode()
{
LOG(LogDebug) << " UIModeController::listen(): Passkey sequence completed, switching UIMode to full";
LOG(LogDebug) <<
" UIModeController::listen(): Passkey sequence completed, switching UIMode to full";
Settings::getInstance()->setString("UIMode", "Full");
Settings::getInstance()->saveFile();
mPassKeyCounter = 0;
@ -102,26 +100,24 @@ bool UIModeController::isUIModeKiosk()
std::string UIModeController::getFormattedPassKeyStr()
{
// supported sequence-inputs: u (up), d (down), l (left), r (right), a, b, x, y
// Supported sequence-inputs: u (up), d (down), l (left), r (right), a, b, x, y.
std::string out = "";
for (auto c : mPassKeySequence)
{
out += (out == "") ? "" : ", "; // add a comma after the first entry
for (auto c : mPassKeySequence) {
out += (out == "") ? "" : ", "; // Add a comma after the first entry.
switch (c)
{
switch (c) {
case 'u':
out += Utils::String::unicode2Chars(0x2191); // arrow pointing up
out += Utils::String::unicode2Chars(0x2191); // Arrow pointing up.
break;
case 'd':
out += Utils::String::unicode2Chars(0x2193); // arrow pointing down
out += Utils::String::unicode2Chars(0x2193); // Arrow pointing down.
break;
case 'l':
out += Utils::String::unicode2Chars(0x2190); // arrow pointing left
out += Utils::String::unicode2Chars(0x2190); // Arrow pointing left.
break;
case 'r':
out += Utils::String::unicode2Chars(0x2192); // arrow pointing right
out += Utils::String::unicode2Chars(0x2192); // Arrow pointing right.
break;
case 'a':
out += "A";
@ -140,29 +136,26 @@ std::string UIModeController::getFormattedPassKeyStr()
return out;
}
void UIModeController::logInput(InputConfig * config, Input input)
{
std::string mapname = "";
std::vector<std::string> maps = config->getMappedTo(input);
for( auto mn : maps)
{
for (auto mn : maps) {
mapname += mn;
mapname += ", ";
}
LOG(LogDebug) << "UIModeController::logInput( " << config->getDeviceName() <<" ):" << input.string() << ", isMappedTo= " << mapname << ", value=" << input.value;
LOG(LogDebug) << "UIModeController::logInput( " << config->getDeviceName() <<
" ):" << input.string() << ", isMappedTo= " << mapname << ", value=" << input.value;
}
bool UIModeController::isValidInput(InputConfig * config, Input input)
{
if((config->getMappedTo(input).size() == 0) || // not a mapped input, so ignore.
(input.type == TYPE_HAT) || // ignore all HAT inputs
(!input.value)) // not a key-down event
{
if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore..
(input.type == TYPE_HAT) || // Ignore all hat inputs.
(!input.value)) // Not a key-down event.
return false;
}
else
{
return true;
}
}
}

View file

@ -1,3 +1,10 @@
//
// UIModeController.h
//
// Handling of application user interface modes (full, kiosk and kid).
// This includes switching the mode when the UI mode passkey was used.
//
#pragma once
#ifndef ES_APP_VIEWS_UI_MODE_CONTROLLER_H
#define ES_APP_VIEWS_UI_MODE_CONTROLLER_H
@ -11,11 +18,13 @@ class ViewController;
struct Input;
class UIModeController {
class UIModeController
{
public:
static UIModeController* getInstance();
// Monitor input for UI mode change, returns true (consumes input) when UI mode change is triggered.
// Monitor input for UI mode change, returns true (consumes input) when a UI mode
// change is triggered.
bool listen(InputConfig* config, Input input);
// Get the current Passphrase as a (unicode) formatted, comma-separated, string.
@ -28,22 +37,24 @@ public:
bool isUIModeKid();
bool isUIModeKiosk();
inline std::vector<std::string> getUIModes() { return mUIModes; };
private:
UIModeController();
bool inputIsMatch(InputConfig * config, Input input);
bool isValidInput(InputConfig * config, Input input);
void logInput(InputConfig * config, Input input);
// Return UI mode to 'FULL'
// Return UI mode to 'full'.
void unlockUIMode();
static UIModeController * sInstance;
const std::vector<std::string> mUIModes = { "Full", "Kiosk", "Kid" };
// default passkeyseq = "uuddlrlrba", as defined in the setting 'UIMode_passkey'.
// Default passkeyseq = "uuddlrlrba", as defined in the setting 'UIMode_passkey'.
std::string mPassKeySequence;
int mPassKeyCounter;
const std::vector<std::string> mInputVals = { "up", "down", "left", "right", "a", "b", "x", "y" };
const std::vector<std::string> mInputVals =
{ "up", "down", "left", "right", "a", "b", "x", "y" };
std::string mCurrentUIMode;
};

View file

@ -1,11 +1,21 @@
//
// ComponentGrid.cpp
//
// Providing basic layout of other components in an X*Y grid.
//
#include "components/ComponentGrid.h"
#include "Settings.h"
using namespace GridFlags;
ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions) : GuiComponent(window),
mGridSize(gridDimensions), mCursor(0, 0)
ComponentGrid::ComponentGrid(
Window* window,
const Vector2i& gridDimensions)
: GuiComponent(window),
mGridSize(gridDimensions),
mCursor(0, 0)
{
assert(gridDimensions.x() > 0 && gridDimensions.y() > 0);
@ -13,9 +23,9 @@ ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions) : G
mColWidths = new float[gridDimensions.x()];
mRowHeights = new float[gridDimensions.y()];
for(int x = 0; x < gridDimensions.x(); x++)
for (int x = 0; x < gridDimensions.x(); x++)
mColWidths[x] = 0;
for(int y = 0; y < gridDimensions.y(); y++)
for (int y = 0; y < gridDimensions.y(); y++)
mRowHeights[y] = 0;
}
@ -27,16 +37,15 @@ ComponentGrid::~ComponentGrid()
float ComponentGrid::getColWidth(int col)
{
if(mColWidths[col] != 0)
if (mColWidths[col] != 0)
return mColWidths[col] * mSize.x();
// calculate automatic width
// Calculate automatic width.
float freeWidthPerc = 1;
int between = 0;
for(int x = 0; x < mGridSize.x(); x++)
{
freeWidthPerc -= mColWidths[x]; // if it's 0 it won't do anything
if(mColWidths[x] == 0)
for (int x = 0; x < mGridSize.x(); x++) {
freeWidthPerc -= mColWidths[x]; // If it's 0 it won't do anything.
if (mColWidths[x] == 0)
between++;
}
@ -45,16 +54,15 @@ float ComponentGrid::getColWidth(int col)
float ComponentGrid::getRowHeight(int row)
{
if(mRowHeights[row] != 0)
if (mRowHeights[row] != 0)
return mRowHeights[row] * mSize.y();
// calculate automatic height
// Calculate automatic height.
float freeHeightPerc = 1;
int between = 0;
for(int y = 0; y < mGridSize.y(); y++)
{
freeHeightPerc -= mRowHeights[y]; // if it's 0 it won't do anything
if(mRowHeights[y] == 0)
for (int y = 0; y < mGridSize.y(); y++) {
freeHeightPerc -= mRowHeights[y]; // If it's 0 it won't do anything.
if (mRowHeights[y] == 0)
between++;
}
@ -67,7 +75,7 @@ void ComponentGrid::setColWidthPerc(int col, float width, bool update)
assert(col >= 0 && col < mGridSize.x());
mColWidths[col] = width;
if(update)
if (update)
onSizeChanged();
}
@ -77,12 +85,18 @@ void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
assert(row >= 0 && row < mGridSize.y());
mRowHeights[row] = height;
if(update)
if (update)
onSizeChanged();
}
void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Vector2i& pos, bool canFocus, bool resize, const Vector2i& size,
unsigned int border, GridFlags::UpdateType updateType)
void ComponentGrid::setEntry(
const std::shared_ptr<GuiComponent>& comp,
const Vector2i& pos,
bool canFocus,
bool resize,
const Vector2i& size,
unsigned int border,
GridFlags::UpdateType updateType)
{
assert(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
assert(comp != nullptr);
@ -93,8 +107,7 @@ void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Ve
addChild(comp.get());
if(!cursorValid() && canFocus)
{
if (!cursorValid() && canFocus) {
auto origCursor = mCursor;
mCursor = pos;
onCursorMoved(origCursor, mCursor);
@ -106,10 +119,8 @@ void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Ve
bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
{
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
if(it->component == comp)
{
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (it->component == comp) {
removeChild(comp.get());
mCells.erase(it);
return true;
@ -121,25 +132,25 @@ bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
void ComponentGrid::updateCellComponent(const GridEntry& cell)
{
// size
// Size.
Vector2f size(0, 0);
for(int x = cell.pos.x(); x < cell.pos.x() + cell.dim.x(); x++)
for (int x = cell.pos.x(); x < cell.pos.x() + cell.dim.x(); x++)
size[0] += getColWidth(x);
for(int y = cell.pos.y(); y < cell.pos.y() + cell.dim.y(); y++)
for (int y = cell.pos.y(); y < cell.pos.y() + cell.dim.y(); y++)
size[1] += getRowHeight(y);
if(cell.resize)
if (cell.resize)
cell.component->setSize(size);
// position
// find top left corner
// Position.
// Find top left corner.
Vector3f pos(0, 0, 0);
for(int x = 0; x < cell.pos.x(); x++)
for (int x = 0; x < cell.pos.x(); x++)
pos[0] += getColWidth(x);
for(int y = 0; y < cell.pos.y(); y++)
for (int y = 0; y < cell.pos.y(); y++)
pos[1] += getRowHeight(y);
// center component
// Center component.
pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2;
pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2;
@ -155,49 +166,44 @@ void ComponentGrid::updateSeparators()
Vector2f pos;
Vector2f size;
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
if(!it->border && !drawAll)
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (!it->border && !drawAll)
continue;
// find component position + size
// Find component position + size.
pos = Vector2f(0, 0);
size = Vector2f(0, 0);
for(int x = 0; x < it->pos.x(); x++)
for (int x = 0; x < it->pos.x(); x++)
pos[0] += getColWidth(x);
for(int y = 0; y < it->pos.y(); y++)
for (int y = 0; y < it->pos.y(); y++)
pos[1] += getRowHeight(y);
for(int x = it->pos.x(); x < it->pos.x() + it->dim.x(); x++)
for (int x = it->pos.x(); x < it->pos.x() + it->dim.x(); x++)
size[0] += getColWidth(x);
for(int y = it->pos.y(); y < it->pos.y() + it->dim.y(); y++)
for (int y = it->pos.y(); y < it->pos.y() + it->dim.y(); y++)
size[1] += getRowHeight(y);
if(it->border & BORDER_TOP || drawAll)
{
mLines.push_back( { { pos.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
if (it->border & BORDER_TOP || drawAll) {
mLines.push_back( { { pos.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
}
if(it->border & BORDER_BOTTOM || drawAll)
{
mLines.push_back( { { pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x() + size.x(), mLines.back().pos.y() }, { 0.0f, 0.0f }, color } );
if (it->border & BORDER_BOTTOM || drawAll) {
mLines.push_back( { { pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x() + size.x(), mLines.back().pos.y() }, { 0.0f, 0.0f }, color } );
}
if(it->border & BORDER_LEFT || drawAll)
{
mLines.push_back( { { pos.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
if (it->border & BORDER_LEFT || drawAll) {
mLines.push_back( { { pos.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
}
if(it->border & BORDER_RIGHT || drawAll)
{
mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { mLines.back().pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
if (it->border & BORDER_RIGHT || drawAll) {
mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
mLines.push_back( { { mLines.back().pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
}
}
}
void ComponentGrid::onSizeChanged()
{
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
for (auto it = mCells.cbegin(); it != mCells.cend(); it++)
updateCellComponent(*it);
updateSeparators();
@ -207,14 +213,13 @@ const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
{
assert(x >= 0 && x < mGridSize.x() && y >= 0 && y < mGridSize.y());
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
int xmin = it->pos.x();
int xmax = xmin + it->dim.x();
int ymin = it->pos.y();
int ymax = ymin + it->dim.y();
if(x >= xmin && y >= ymin && x < xmax && y < ymax)
if (x >= xmin && y >= ymin && x < xmax && y < ymax)
return &(*it);
}
@ -224,41 +229,34 @@ const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
bool ComponentGrid::input(InputConfig* config, Input input)
{
const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->component->input(config, input))
if (cursorEntry && cursorEntry->component->input(config, input))
return true;
if(!input.value)
if (!input.value)
return false;
if(config->isMappedLike("down", input))
{
if (config->isMappedLike("down", input))
return moveCursor(Vector2i(0, 1));
}
if(config->isMappedLike("up", input))
{
if (config->isMappedLike("up", input))
return moveCursor(Vector2i(0, -1));
}
if(config->isMappedLike("left", input))
{
if (config->isMappedLike("left", input))
return moveCursor(Vector2i(-1, 0));
}
if(config->isMappedLike("right", input))
{
if (config->isMappedLike("right", input))
return moveCursor(Vector2i(1, 0));
}
return false;
}
void ComponentGrid::resetCursor()
{
if(!mCells.size())
if (!mCells.size())
return;
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
if(it->canFocus)
{
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (it->canFocus) {
Vector2i origCursor = mCursor;
mCursor = it->pos;
onCursorMoved(origCursor, mCursor);
@ -272,51 +270,42 @@ bool ComponentGrid::moveCursor(Vector2i dir)
assert(dir.x() || dir.y());
const Vector2i origCursor = mCursor;
const GridEntry* currentCursorEntry = getCellAt(mCursor);
Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
while(mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y())
{
while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
mCursor.y() < mGridSize.y()) {
mCursor = mCursor + dir;
Vector2i curDirPos = mCursor;
const GridEntry* cursorEntry;
//spread out on search axis+
while(mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()
&& mCursor.x() >= 0 && mCursor.y() >= 0)
{
// Spread out on search axis+
while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()
&& mCursor.x() >= 0 && mCursor.y() >= 0) {
cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry)
{
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
onCursorMoved(origCursor, mCursor);
return true;
}
mCursor += searchAxis;
}
//now again on search axis-
// Now again on search axis-
mCursor = curDirPos;
while(mCursor.x() >= 0 && mCursor.y() >= 0
&& mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y())
{
while (mCursor.x() >= 0 && mCursor.y() >= 0
&& mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) {
cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry)
{
if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
onCursorMoved(origCursor, mCursor);
return true;
}
mCursor -= searchAxis;
}
mCursor = curDirPos;
}
//failed to find another focusable element in this direction
// Failed to find another focusable element in this direction.
mCursor = origCursor;
return false;
}
@ -324,14 +313,14 @@ bool ComponentGrid::moveCursor(Vector2i dir)
void ComponentGrid::onFocusLost()
{
const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry)
if (cursorEntry)
cursorEntry->component->onFocusLost();
}
void ComponentGrid::onFocusGained()
{
const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry)
if (cursorEntry)
cursorEntry->component->onFocusGained();
}
@ -343,11 +332,12 @@ bool ComponentGrid::cursorValid()
void ComponentGrid::update(int deltaTime)
{
// update ALL THE THINGS
// Update ALL THE THINGS.
const GridEntry* cursorEntry = getCellAt(mCursor);
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
for (auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
if(it->updateType == UPDATE_ALWAYS || (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
if (it->updateType == UPDATE_ALWAYS ||
(it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
it->component->update(deltaTime);
}
}
@ -358,9 +348,8 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
renderChildren(trans);
// draw cell separators
if(mLines.size())
{
// Draw cell separators.
if (mLines.size()) {
Renderer::setMatrix(trans);
Renderer::bindTexture(0);
Renderer::drawLines(&mLines[0], mLines.size());
@ -370,18 +359,18 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
void ComponentGrid::textInput(const char* text)
{
const GridEntry* selectedEntry = getCellAt(mCursor);
if(selectedEntry != NULL && selectedEntry->canFocus)
if (selectedEntry != NULL && selectedEntry->canFocus)
selectedEntry->component->textInput(text);
}
void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
{
const GridEntry* cell = getCellAt(from);
if(cell)
if (cell)
cell->component->onFocusLost();
cell = getCellAt(to);
if(cell)
if (cell)
cell->component->onFocusGained();
updateHelpPrompts();
@ -389,10 +378,8 @@ void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
{
for(auto it = mCells.cbegin(); it != mCells.cend(); it++)
{
if(it->component == comp)
{
for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
if (it->component == comp) {
Vector2i oldCursor = mCursor;
mCursor = it->pos;
onCursorMoved(oldCursor, mCursor);
@ -400,7 +387,7 @@ void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
}
}
// component not found!!
// Component not found!!
assert(false);
}
@ -408,32 +395,30 @@ std::vector<HelpPrompt> ComponentGrid::getHelpPrompts()
{
std::vector<HelpPrompt> prompts;
const GridEntry* e = getCellAt(mCursor);
if(e)
if (e)
prompts = e->component->getHelpPrompts();
bool canScrollVert = mGridSize.y() > 1;
bool canScrollHoriz = mGridSize.x() > 1;
for(auto it = prompts.cbegin(); it != prompts.cend(); it++)
{
if(it->first == "up/down/left/right")
{
for (auto it = prompts.cbegin(); it != prompts.cend(); it++) {
if (it->first == "up/down/left/right") {
canScrollHoriz = false;
canScrollVert = false;
break;
}else if(it->first == "up/down")
{
}
else if (it->first == "up/down") {
canScrollVert = false;
}else if(it->first == "left/right")
{
}
else if (it->first == "left/right") {
canScrollHoriz = false;
}
}
if(canScrollHoriz && canScrollVert)
if (canScrollHoriz && canScrollVert)
prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
else if(canScrollHoriz)
else if (canScrollHoriz)
prompts.push_back(HelpPrompt("left/right", "choose"));
else if(canScrollVert)
else if (canScrollVert)
prompts.push_back(HelpPrompt("up/down", "choose"));
return prompts;

View file

@ -1,3 +1,9 @@
//
// ComponentGrid.h
//
// Providing basic layout of other components in an X*Y grid.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_COMPONENT_GRID_H
#define ES_CORE_COMPONENTS_COMPONENT_GRID_H
@ -8,15 +14,13 @@
namespace GridFlags
{
enum UpdateType
{
enum UpdateType {
UPDATE_ALWAYS,
UPDATE_WHEN_SELECTED,
UPDATE_NEVER
};
enum Border : unsigned int
{
enum Border : unsigned int {
BORDER_NONE = 0,
BORDER_TOP = 1,
@ -35,8 +39,14 @@ public:
bool removeEntry(const std::shared_ptr<GuiComponent>& comp);
void setEntry(const std::shared_ptr<GuiComponent>& comp, const Vector2i& pos, bool canFocus, bool resize = true,
const Vector2i& size = Vector2i(1, 1), unsigned int border = GridFlags::BORDER_NONE, GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS);
void setEntry(
const std::shared_ptr<GuiComponent>& comp,
const Vector2i& pos,
bool canFocus,
bool resize = true,
const Vector2i& size = Vector2i(1, 1),
unsigned int border = GridFlags::BORDER_NONE,
GridFlags::UpdateType updateType = GridFlags::UPDATE_ALWAYS);
void textInput(const char* text) override;
bool input(InputConfig* config, Input input) override;
@ -50,8 +60,11 @@ public:
float getColWidth(int col);
float getRowHeight(int row);
void setColWidthPerc(int col, float width, bool update = true); // if update is false, will not call an onSizeChanged() which triggers a (potentially costly) repositioning + resizing of every element
void setRowHeightPerc(int row, float height, bool update = true); // if update is false, will not call an onSizeChanged() which triggers a (potentially costly) repositioning + resizing of every element
// If update is false, will not call an onSizeChanged() which triggers
// a (potentially costly) repositioning + resizing of every element.
void setColWidthPerc(int col, float width, bool update = true);
// Dito.
void setRowHeightPerc(int row, float height, bool update = true);
bool moveCursor(Vector2i dir);
void setCursorTo(const std::shared_ptr<GuiComponent>& comp);
@ -84,7 +97,8 @@ private:
GridEntry(const Vector2i& p = Vector2i::Zero(), const Vector2i& d = Vector2i::Zero(),
const std::shared_ptr<GuiComponent>& cmp = nullptr, bool f = false, bool r = true,
GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, unsigned int b = GridFlags::BORDER_NONE) :
GridFlags::UpdateType u = GridFlags::UPDATE_ALWAYS, unsigned int b =
GridFlags::BORDER_NONE) :
pos(p), dim(d), component(cmp), canFocus(f), resize(r), updateType(u), border(b)
{};
@ -99,12 +113,13 @@ private:
std::vector<Renderer::Vertex> mLines;
// Update position & size
// Update position & size.
void updateCellComponent(const GridEntry& cell);
void updateSeparators();
const GridEntry* getCellAt(int x, int y) const;
inline const GridEntry* getCellAt(const Vector2i& pos) const { return getCellAt(pos.x(), pos.y()); }
inline const GridEntry* getCellAt(const Vector2i& pos) const
{ return getCellAt(pos.x(), pos.y()); }
Vector2i mGridSize;

View file

@ -1,8 +1,15 @@
//
// ComponentList.cpp
//
// Used to lay out and navigate lists in GUI menus.
//
#include "components/ComponentList.h"
#define TOTAL_HORIZONTAL_PADDING_PX 20
ComponentList::ComponentList(Window* window) : IList<ComponentListRow, void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
ComponentList::ComponentList(Window* window) : IList<ComponentListRow,
void*>(window, LIST_SCROLL_STYLE_SLOW, LIST_NEVER_LOOP)
{
mSelectorBarOffset = 0;
mCameraOffset = 0;
@ -18,14 +25,14 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
this->add(e);
for(auto it = mEntries.back().data.elements.cbegin(); it != mEntries.back().data.elements.cend(); it++)
for (auto it = mEntries.back().data.elements.cbegin();
it != mEntries.back().data.elements.cend(); it++)
addChild(it->component.get());
updateElementSize(mEntries.back().data);
updateElementPosition(mEntries.back().data);
if(setCursorHere)
{
if (setCursorHere) {
mCursor = (int)mEntries.size() - 1;
onCursorChanged(CURSOR_STOPPED);
}
@ -33,8 +40,7 @@ void ComponentList::addRow(const ComponentListRow& row, bool setCursorHere)
void ComponentList::onSizeChanged()
{
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
{
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++) {
updateElementSize(it->data);
updateElementPosition(it->data);
}
@ -54,38 +60,33 @@ void ComponentList::onFocusGained()
bool ComponentList::input(InputConfig* config, Input input)
{
if(size() == 0)
if (size() == 0)
return false;
// give it to the current row's input handler
if(mEntries.at(mCursor).data.input_handler)
{
if(mEntries.at(mCursor).data.input_handler(config, input))
// Give it to the current row's input handler.
if (mEntries.at(mCursor).data.input_handler) {
if (mEntries.at(mCursor).data.input_handler(config, input))
return true;
}else{
// no input handler assigned, do the default, which is to give it to the rightmost element in the row
}
else {
// No input handler assigned, do the default, which is to give it
// to the rightmost element in the row.
auto& row = mEntries.at(mCursor).data;
if(row.elements.size())
{
if(row.elements.back().component->input(config, input))
if (row.elements.size()) {
if (row.elements.back().component->input(config, input))
return true;
}
}
// input handler didn't consume the input - try to scroll
if(config->isMappedLike("up", input))
{
// Input handler didn't consume the input - try to scroll.
if (config->isMappedLike("up", input))
return listInput(input.value != 0 ? -1 : 0);
}else if(config->isMappedLike("down", input))
{
else if (config->isMappedLike("down", input))
return listInput(input.value != 0 ? 1 : 0);
}else if(config->isMappedLike("leftshoulder", input))
{
else if (config->isMappedLike("leftshoulder", input))
return listInput(input.value != 0 ? -6 : 0);
}else if(config->isMappedLike("rightshoulder", input)){
else if (config->isMappedLike("rightshoulder", input))
return listInput(input.value != 0 ? 6 : 0);
}
return false;
}
@ -94,36 +95,33 @@ void ComponentList::update(int deltaTime)
{
listUpdate(deltaTime);
if(size())
{
// update our currently selected row
for(auto it = mEntries.at(mCursor).data.elements.cbegin(); it != mEntries.at(mCursor).data.elements.cend(); it++)
if (size()) {
// Update our currently selected row.
for (auto it = mEntries.at(mCursor).data.elements.cbegin();
it != mEntries.at(mCursor).data.elements.cend(); it++)
it->component->update(deltaTime);
}
}
void ComponentList::onCursorChanged(const CursorState& state)
{
// update the selector bar position
// in the future this might be animated
// Update the selector bar position.
// In the future this might be animated.
mSelectorBarOffset = 0;
for(int i = 0; i < mCursor; i++)
{
for (int i = 0; i < mCursor; i++)
mSelectorBarOffset += getRowHeight(mEntries.at(i).data);
}
updateCameraOffset();
// this is terribly inefficient but we don't know what we came from so...
if(size())
{
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
// This is terribly inefficient but we don't know what we came from so...
if (size()) {
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
it->data.elements.back().component->onFocusLost();
mEntries.at(mCursor).data.elements.back().component->onFocusGained();
}
if(mCursorChangedCallback)
if (mCursorChangedCallback)
mCursorChangedCallback(state);
updateHelpPrompts();
@ -131,110 +129,109 @@ void ComponentList::onCursorChanged(const CursorState& state)
void ComponentList::updateCameraOffset()
{
// move the camera to scroll
// Move the camera to scroll.
const float totalHeight = getTotalRowHeight();
if(totalHeight > mSize.y())
{
float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data)/2 - (mSize.y() / 2);
if (totalHeight > mSize.y()) {
float target = mSelectorBarOffset + getRowHeight(mEntries.at(mCursor).data)/2 -
(mSize.y() / 2);
// clamp it
// Clamp it.
mCameraOffset = 0;
unsigned int i = 0;
while(mCameraOffset < target && i < mEntries.size())
{
while (mCameraOffset < target && i < mEntries.size()) {
mCameraOffset += getRowHeight(mEntries.at(i).data);
i++;
}
if(mCameraOffset < 0)
if (mCameraOffset < 0)
mCameraOffset = 0;
else if(mCameraOffset + mSize.y() > totalHeight)
else if (mCameraOffset + mSize.y() > totalHeight)
mCameraOffset = totalHeight - mSize.y();
}else{
}
else {
mCameraOffset = 0;
}
}
void ComponentList::render(const Transform4x4f& parentTrans)
{
if(!size())
if (!size())
return;
Transform4x4f trans = parentTrans * getTransform();
// clip everything to be inside our bounds
// Clip everything to be inside our bounds.
Vector3f dim(mSize.x(), mSize.y(), 0);
dim = trans * dim - trans.translation();
Renderer::pushClipRect(Vector2i((int)trans.translation().x(), (int)trans.translation().y()),
Vector2i((int)Math::round(dim.x()), (int)Math::round(dim.y() + 1)));
// scroll the camera
// Scroll the camera.
trans.translate(Vector3f(0, -Math::round(mCameraOffset), 0));
// draw our entries
// Draw our entries.
std::vector<GuiComponent*> drawAfterCursor;
bool drawAll;
for(unsigned int i = 0; i < mEntries.size(); i++)
{
for (unsigned int i = 0; i < mEntries.size(); i++) {
auto& entry = mEntries.at(i);
drawAll = !mFocused || i != (unsigned int)mCursor;
for(auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++)
{
if(drawAll || it->invert_when_selected)
{
for (auto it = entry.data.elements.cbegin(); it != entry.data.elements.cend(); it++) {
if (drawAll || it->invert_when_selected)
it->component->render(trans);
}else{
else
drawAfterCursor.push_back(it->component.get());
}
}
}
// custom rendering
// Custom rendering.
Renderer::setMatrix(trans);
// draw selector bar
if(mFocused)
{
// inversion: src * (1 - dst) + dst * 0 = where src = 1
// need a function that goes roughly 0x777777 -> 0xFFFFFF
// Draw selector bar.
if (mFocused) {
// Inversion: src * (1 - dst) + dst * 0 = where src = 1
// Need a function that goes roughly 0x777777 -> 0xFFFFFF
// and 0xFFFFFF -> 0x777777
// (1 - dst) + 0x77
const float selectedRowHeight = getRowHeight(mEntries.at(mCursor).data);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0xFFFFFFFF, 0xFFFFFFFF, false, Renderer::Blend::ONE_MINUS_DST_COLOR, Renderer::Blend::ZERO);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight, 0x777777FF, 0x777777FF, false, Renderer::Blend::ONE, Renderer::Blend::ONE);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight,
0xFFFFFFFF, 0xFFFFFFFF, false, Renderer::Blend::ONE_MINUS_DST_COLOR,
Renderer::Blend::ZERO);
Renderer::drawRect(0.0f, mSelectorBarOffset, mSize.x(), selectedRowHeight,
0x777777FF, 0x777777FF, false, Renderer::Blend::ONE,
Renderer::Blend::ONE);
// hack to draw 2px dark on left/right of the bar
Renderer::drawRect(0.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, 0x878787FF, 0x878787FF);
Renderer::drawRect(mSize.x() - 2.0f, mSelectorBarOffset, 2.0f, selectedRowHeight, 0x878787FF, 0x878787FF);
// Hack to draw 2px dark on left/right of the bar.
Renderer::drawRect(0.0f, mSelectorBarOffset, 2.0f, selectedRowHeight,
0x878787FF, 0x878787FF);
Renderer::drawRect(mSize.x() - 2.0f, mSelectorBarOffset, 2.0f, selectedRowHeight,
0x878787FF, 0x878787FF);
for(auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++)
for (auto it = drawAfterCursor.cbegin(); it != drawAfterCursor.cend(); it++)
(*it)->render(trans);
// reset matrix if one of these components changed it
if(drawAfterCursor.size())
// Reset matrix if one of these components changed it.
if (drawAfterCursor.size())
Renderer::setMatrix(trans);
}
// draw separators
// Draw separators.
float y = 0;
for(unsigned int i = 0; i < mEntries.size(); i++)
{
for (unsigned int i = 0; i < mEntries.size(); i++) {
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF);
y += getRowHeight(mEntries.at(i).data);
}
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF);
Renderer::drawRect(0.0f, y, mSize.x(), 1.0f, 0xC6C7C6FF, 0xC6C7C6FF);
Renderer::popClipRect();
}
float ComponentList::getRowHeight(const ComponentListRow& row) const
{
// returns the highest component height found in the row
// Returns the highest component height found in the row.
float height = 0;
for(unsigned int i = 0; i < row.elements.size(); i++)
{
if(row.elements.at(i).component->getSize().y() > height)
for (unsigned int i = 0; i < row.elements.size(); i++) {
if (row.elements.at(i).component->getSize().y() > height)
height = row.elements.at(i).component->getSize().y();
}
@ -244,10 +241,8 @@ float ComponentList::getRowHeight(const ComponentListRow& row) const
float ComponentList::getTotalRowHeight() const
{
float height = 0;
for(auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
{
for (auto it = mEntries.cbegin(); it != mEntries.cend(); it++)
height += getRowHeight(it->data);
}
return height;
}
@ -255,20 +250,17 @@ float ComponentList::getTotalRowHeight() const
void ComponentList::updateElementPosition(const ComponentListRow& row)
{
float yOffset = 0;
for(auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; it++)
{
for (auto it = mEntries.cbegin(); it != mEntries.cend() && &it->data != &row; it++)
yOffset += getRowHeight(it->data);
}
// assumes updateElementSize has already been called
// Assumes updateElementSize has already been called.
float rowHeight = getRowHeight(row);
float x = TOTAL_HORIZONTAL_PADDING_PX / 2;
for(unsigned int i = 0; i < row.elements.size(); i++)
{
for (unsigned int i = 0; i < row.elements.size(); i++) {
const auto comp = row.elements.at(i).component;
// center vertically
// Center vertically.
comp->setPosition(x, (rowHeight - comp->getSize().y()) / 2 + yOffset);
x += comp->getSize().x();
}
@ -279,25 +271,22 @@ void ComponentList::updateElementSize(const ComponentListRow& row)
float width = mSize.x() - TOTAL_HORIZONTAL_PADDING_PX;
std::vector< std::shared_ptr<GuiComponent> > resizeVec;
for(auto it = row.elements.cbegin(); it != row.elements.cend(); it++)
{
if(it->resize_width)
for (auto it = row.elements.cbegin(); it != row.elements.cend(); it++) {
if (it->resize_width)
resizeVec.push_back(it->component);
else
width -= it->component->getSize().x();
}
// redistribute the "unused" width equally among the components with resize_width set to true
// Redistribute the "unused" width equally among the components with resize_width set to true.
width = width / resizeVec.size();
for(auto it = resizeVec.cbegin(); it != resizeVec.cend(); it++)
{
for (auto it = resizeVec.cbegin(); it != resizeVec.cend(); it++)
(*it)->setSize(width, (*it)->getSize().y());
}
}
void ComponentList::textInput(const char* text)
{
if(!size())
if (!size())
return;
mEntries.at(mCursor).data.elements.back().component->textInput(text);
@ -305,24 +294,21 @@ void ComponentList::textInput(const char* text)
std::vector<HelpPrompt> ComponentList::getHelpPrompts()
{
if(!size())
if (!size())
return std::vector<HelpPrompt>();
std::vector<HelpPrompt> prompts = mEntries.at(mCursor).data.elements.back().component->getHelpPrompts();
std::vector<HelpPrompt> prompts =
mEntries.at(mCursor).data.elements.back().component->getHelpPrompts();
if(size() > 1)
{
if (size() > 1) {
bool addMovePrompt = true;
for(auto it = prompts.cbegin(); it != prompts.cend(); it++)
{
if(it->first == "up/down" || it->first == "up/down/left/right")
{
for (auto it = prompts.cbegin(); it != prompts.cend(); it++) {
if (it->first == "up/down" || it->first == "up/down/left/right") {
addMovePrompt = false;
break;
}
}
if(addMovePrompt)
if (addMovePrompt)
prompts.push_back(HelpPrompt("up/down", "choose"));
}

View file

@ -1,13 +1,23 @@
//
// ComponentList.h
//
// Used to lay out and navigate lists in GUI menus.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_COMPONENT_LIST_H
#define ES_CORE_COMPONENTS_COMPONENT_LIST_H
#include "IList.h"
struct ComponentListElement
{
ComponentListElement(const std::shared_ptr<GuiComponent>& cmp = nullptr, bool resize_w = true, bool inv = true)
: component(cmp), resize_width(resize_w), invert_when_selected(inv) { };
struct ComponentListElement {
ComponentListElement(
const std::shared_ptr<GuiComponent>& cmp = nullptr,
bool resize_w = true,
bool inv = true)
: component(cmp),
resize_width(resize_w),
invert_when_selected(inv) {};
std::shared_ptr<GuiComponent> component;
bool resize_width;
@ -18,23 +28,24 @@ struct ComponentListRow
{
std::vector<ComponentListElement> elements;
// The input handler is called when the user enters any input while this row is highlighted (including up/down).
// The input handler is called when the user enters any input while this row is
// highlighted (including up/down).
// Return false to let the list try to use it or true if the input has been consumed.
// If no input handler is supplied (input_handler == nullptr), the default behavior is to forward the input to
// the rightmost element in the currently selected row.
// If no input handler is supplied (input_handler == nullptr), the default behavior is
// to forward the input to the rightmost element in the currently selected row.
std::function<bool(InputConfig*, Input)> input_handler;
inline void addElement(const std::shared_ptr<GuiComponent>& component, bool resize_width, bool invert_when_selected = true)
inline void addElement(const std::shared_ptr<GuiComponent>& component,
bool resize_width, bool invert_when_selected = true)
{
elements.push_back(ComponentListElement(component, resize_width, invert_when_selected));
}
// Utility method for making an input handler for "when the users presses A on this, do func."
// Utility method for making an input handler for "when the users presses A on this, do func".
inline void makeAcceptInputHandler(const std::function<void()>& func)
{
input_handler = [func](InputConfig* config, Input input) -> bool {
if(config->isMappedTo("a", input) && input.value != 0)
{
if(config->isMappedTo("a", input) && input.value != 0) {
func();
return true;
}
@ -66,8 +77,10 @@ public:
float getTotalRowHeight() const;
inline float getRowHeight(int row) const { return getRowHeight(mEntries.at(row).data); }
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& callback) { mCursorChangedCallback = callback; };
inline const std::function<void(CursorState state)>& getCursorChangedCallback() const { return mCursorChangedCallback; };
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& callback)
{ mCursorChangedCallback = callback; };
inline const std::function<void(CursorState state)>& getCursorChangedCallback() const
{ return mCursorChangedCallback; };
protected:
void onCursorChanged(const CursorState& state) override;

View file

@ -1,16 +1,32 @@
//
// DateTimeComponent.cpp
//
// Date and time component.
//
#include "components/DateTimeComponent.h"
#include "utils/StringUtil.h"
#include "Log.h"
#include "Settings.h"
DateTimeComponent::DateTimeComponent(Window* window) : TextComponent(window), mDisplayRelative(false)
DateTimeComponent::DateTimeComponent(Window* window)
: TextComponent(window), mDisplayRelative(false)
{
setFormat("%m/%d/%Y");
}
DateTimeComponent::DateTimeComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Alignment align,
Vector3f pos, Vector2f size, unsigned int bgcolor) : TextComponent(window, text, font, color, align, pos, size, bgcolor), mDisplayRelative(false)
DateTimeComponent::DateTimeComponent(
Window* window,
const std::string& text,
const std::shared_ptr<Font>& font,
unsigned int color,
Alignment align,
Vector3f pos,
Vector2f size,
unsigned int bgcolor)
: TextComponent(window, text, font, color, align, pos, size, bgcolor),
mDisplayRelative(false)
{
setFormat("%m/%d/%Y");
}
@ -41,14 +57,13 @@ void DateTimeComponent::setDisplayRelative(bool displayRelative)
void DateTimeComponent::onTextChanged()
{
mText = getDisplayString();
TextComponent::onTextChanged();
}
std::string DateTimeComponent::getDisplayString() const
{
if (mDisplayRelative) {
//relative time
// Relative time.
if(mTime.getTime() == 0)
return "never";
@ -81,7 +96,8 @@ void DateTimeComponent::render(const Transform4x4f& parentTrans)
}
void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& element, unsigned int properties)
{
GuiComponent::applyTheme(theme, view, element, properties);
@ -106,8 +122,7 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, cons
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 @@
//
// DateTimeComponent.h
//
// Date and time component.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
#define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
@ -12,8 +18,15 @@ class DateTimeComponent : public TextComponent
{
public:
DateTimeComponent(Window* window);
DateTimeComponent(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);
DateTimeComponent(
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 render(const Transform4x4f& parentTrans) override;
@ -23,7 +36,8 @@ public:
void setFormat(const std::string& format);
void setDisplayRelative(bool displayRelative);
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:
void onTextChanged() override;

View file

@ -1,11 +1,26 @@
//
// DateTimeEditComponent.cpp
//
// Date and time edit component.
//
#include "components/DateTimeEditComponent.h"
#include "resources/Font.h"
#include "utils/StringUtil.h"
DateTimeEditComponent::DateTimeEditComponent(Window* window, DisplayMode dispMode) : GuiComponent(window),
mEditing(false), mEditIndex(0), mDisplayMode(dispMode), mRelativeUpdateAccumulator(0),
mColor(0x777777FF), mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)), mUppercase(false), mAutoSize(true)
DateTimeEditComponent::DateTimeEditComponent(
Window* window,
DisplayMode dispMode)
: GuiComponent(window),
mEditing(false),
mEditIndex(0),
mDisplayMode(dispMode),
mRelativeUpdateAccumulator(0),
mColor(0x777777FF),
mFont(Font::get(FONT_SIZE_SMALL, FONT_PATH_LIGHT)),
mUppercase(false),
mAutoSize(true)
{
updateTextCache();
}
@ -18,22 +33,19 @@ void DateTimeEditComponent::setDisplayMode(DisplayMode mode)
bool DateTimeEditComponent::input(InputConfig* config, Input input)
{
if(input.value == 0)
if (input.value == 0)
return false;
if(config->isMappedTo("a", input))
{
if(mDisplayMode != DISP_RELATIVE_TO_NOW) //don't allow editing for relative times
if (config->isMappedTo("a", input)) {
if (mDisplayMode != DISP_RELATIVE_TO_NOW) // Don't allow editing for relative times.
mEditing = !mEditing;
if(mEditing)
{
//started editing
if (mEditing) {
// Started editing.
mTimeBeforeEdit = mTime;
//initialize to now if unset
if(mTime.getTime() == Utils::Time::NOT_A_DATE_TIME)
{
// Initialize to now if unset.
if (mTime.getTime() == Utils::Time::NOT_A_DATE_TIME) {
mTime = Utils::Time::now();
updateTextCache();
}
@ -42,10 +54,8 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
return true;
}
if(mEditing)
{
if(config->isMappedTo("b", input))
{
if (mEditing) {
if (config->isMappedTo("b", input)) {
mEditing = false;
mTime = mTimeBeforeEdit;
updateTextCache();
@ -53,47 +63,45 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
}
int incDir = 0;
if(config->isMappedLike("up", input) || config->isMappedLike("leftshoulder", input))
if (config->isMappedLike("up", input) || config->isMappedLike("leftshoulder", input))
incDir = 1;
else if(config->isMappedLike("down", input) || config->isMappedLike("rightshoulder", input))
else if (config->isMappedLike("down", input) || config->isMappedLike("rightshoulder", input))
incDir = -1;
if(incDir != 0)
{
if (incDir != 0) {
tm new_tm = mTime;
if(mEditIndex == 0)
{
if (mEditIndex == 0) {
new_tm.tm_mon += incDir;
if(new_tm.tm_mon > 11)
if (new_tm.tm_mon > 11)
new_tm.tm_mon = 0;
else if(new_tm.tm_mon < 0)
else if (new_tm.tm_mon < 0)
new_tm.tm_mon = 11;
}
else if(mEditIndex == 1)
{
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
else if (mEditIndex == 1) {
const int days_in_month =
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
new_tm.tm_mday += incDir;
if(new_tm.tm_mday > days_in_month)
if (new_tm.tm_mday > days_in_month)
new_tm.tm_mday = 1;
else if(new_tm.tm_mday < 1)
else if (new_tm.tm_mday < 1)
new_tm.tm_mday = days_in_month;
}
else if(mEditIndex == 2)
{
else if (mEditIndex == 2) {
new_tm.tm_year += incDir;
if(new_tm.tm_year < 0)
if (new_tm.tm_year < 0)
new_tm.tm_year = 0;
}
//validate day
const int days_in_month = Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
if(new_tm.tm_mday > days_in_month)
// Validate day.
const int days_in_month =
Utils::Time::daysInMonth(new_tm.tm_year + 1900, new_tm.tm_mon + 1);
if (new_tm.tm_mday > days_in_month)
new_tm.tm_mday = days_in_month;
mTime = new_tm;
@ -102,18 +110,16 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
return true;
}
if(config->isMappedLike("right", input))
{
if (config->isMappedLike("right", input)) {
mEditIndex++;
if(mEditIndex >= (int)mCursorBoxes.size())
if (mEditIndex >= (int)mCursorBoxes.size())
mEditIndex--;
return true;
}
if(config->isMappedLike("left", input))
{
if (config->isMappedLike("left", input)) {
mEditIndex--;
if(mEditIndex < 0)
if (mEditIndex < 0)
mEditIndex++;
return true;
}
@ -124,11 +130,9 @@ bool DateTimeEditComponent::input(InputConfig* config, Input input)
void DateTimeEditComponent::update(int deltaTime)
{
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
{
if (mDisplayMode == DISP_RELATIVE_TO_NOW) {
mRelativeUpdateAccumulator += deltaTime;
if(mRelativeUpdateAccumulator > 1000)
{
if (mRelativeUpdateAccumulator > 1000) {
mRelativeUpdateAccumulator = 0;
updateTextCache();
}
@ -141,9 +145,8 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
if(mTextCache)
{
// vertically center
if (mTextCache) {
// Vertically center.
Vector3f off(0, (mSize.y() - mTextCache->metrics.size.y()) / 2, 0);
trans.translate(off);
@ -154,13 +157,11 @@ void DateTimeEditComponent::render(const Transform4x4f& parentTrans)
mTextCache->setColor((mColor & 0xFFFFFF00) | getOpacity());
font->renderTextCache(mTextCache.get());
if(mEditing)
{
if(mEditIndex >= 0 && (unsigned int)mEditIndex < mCursorBoxes.size())
{
if (mEditing) {
if (mEditIndex >= 0 && (unsigned int)mEditIndex < mCursorBoxes.size())
Renderer::drawRect(mCursorBoxes[mEditIndex][0], mCursorBoxes[mEditIndex][1],
mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3], 0x00000022, 0x00000022);
}
mCursorBoxes[mEditIndex][2], mCursorBoxes[mEditIndex][3],
0x00000022, 0x00000022);
}
}
}
@ -178,14 +179,12 @@ std::string DateTimeEditComponent::getValue() const
DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode() const
{
/*if(mEditing)
{
if(mDisplayMode == DISP_RELATIVE_TO_NOW)
{
//TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME
return DISP_DATE;
}
}*/
// if (mEditing) {
// if (mDisplayMode == DISP_RELATIVE_TO_NOW) {
// // TODO: if time component == 00:00:00, return DISP_DATE, else return DISP_DATE_TIME.
// return DISP_DATE;
// }
// }
return mDisplayMode;
}
@ -193,20 +192,20 @@ DateTimeEditComponent::DisplayMode DateTimeEditComponent::getCurrentDisplayMode(
std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
{
std::string fmt;
switch(mode)
{
case DISP_DATE:
switch (mode) {
case DISP_DATE: {
fmt = "%m/%d/%Y";
break;
case DISP_DATE_TIME:
if(mTime.getTime() == 0)
}
case DISP_DATE_TIME: {
if (mTime.getTime() == 0)
return "unknown";
fmt = "%m/%d/%Y %H:%M:%S";
break;
case DISP_RELATIVE_TO_NOW:
{
//relative time
if(mTime.getTime() == 0)
}
case DISP_RELATIVE_TO_NOW: {
// Relative time.
if (mTime.getTime() == 0)
return "never";
Utils::Time::DateTime now(Utils::Time::now());
@ -214,14 +213,16 @@ std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
char buf[64];
if(dur.getDays() > 0)
if (dur.getDays() > 0)
sprintf(buf, "%d day%s ago", dur.getDays(), (dur.getDays() > 1) ? "s" : "");
else if(dur.getHours() > 0)
else if (dur.getHours() > 0)
sprintf(buf, "%d hour%s ago", dur.getHours(), (dur.getHours() > 1) ? "s" : "");
else if(dur.getMinutes() > 0)
sprintf(buf, "%d minute%s ago", dur.getMinutes(), (dur.getMinutes() > 1) ? "s" : "");
else if (dur.getMinutes() > 0)
sprintf(buf, "%d minute%s ago", dur.getMinutes(),
(dur.getMinutes() > 1) ? "s" : "");
else
sprintf(buf, "%d second%s ago", dur.getSeconds(), (dur.getSeconds() > 1) ? "s" : "");
sprintf(buf, "%d second%s ago", dur.getSeconds(),
(dur.getSeconds() > 1) ? "s" : "");
return std::string(buf);
}
@ -233,7 +234,7 @@ std::string DateTimeEditComponent::getDisplayString(DisplayMode mode) const
std::shared_ptr<Font> DateTimeEditComponent::getFont() const
{
if(mFont)
if (mFont)
return mFont;
return Font::get(FONT_SIZE_MEDIUM);
@ -242,50 +243,51 @@ std::shared_ptr<Font> DateTimeEditComponent::getFont() const
void DateTimeEditComponent::updateTextCache()
{
DisplayMode mode = getCurrentDisplayMode();
const std::string dispString = mUppercase ? Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode);
const std::string dispString = mUppercase ?
Utils::String::toUpper(getDisplayString(mode)) : getDisplayString(mode);
std::shared_ptr<Font> font = getFont();
mTextCache = std::unique_ptr<TextCache>(font->buildTextCache(dispString, 0, 0, mColor));
if(mAutoSize)
{
if (mAutoSize) {
mSize = mTextCache->metrics.size;
mAutoSize = false;
if(getParent())
if (getParent())
getParent()->onSizeChanged();
}
//set up cursor positions
// Set up cursor positions.
mCursorBoxes.clear();
if(dispString.empty() || mode == DISP_RELATIVE_TO_NOW)
if (dispString.empty() || mode == DISP_RELATIVE_TO_NOW)
return;
//month
// Month.
Vector2f start(0, 0);
Vector2f end = font->sizeText(dispString.substr(0, 2));
Vector2f diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
//day
// Day.
start[0] = font->sizeText(dispString.substr(0, 3)).x();
end = font->sizeText(dispString.substr(0, 5));
diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
//year
// Year.
start[0] = font->sizeText(dispString.substr(0, 6)).x();
end = font->sizeText(dispString.substr(0, 10));
diff = end - start;
mCursorBoxes.push_back(Vector4f(start[0], start[1], diff[0], diff[1]));
//if mode == DISP_DATE_TIME do times too but I don't wanna do the logic for editing times because no one will ever use it so screw it
// The logic for handling time for 'mode = DISP_DATE_TIME' is missing, but
// nobody will use it anyway so it's not implemented.
}
void DateTimeEditComponent::setColor(unsigned int color)
{
mColor = color;
if(mTextCache)
if (mTextCache)
mTextCache->setColor(color);
}
@ -307,26 +309,28 @@ void DateTimeEditComponent::setUppercase(bool uppercase)
updateTextCache();
}
void DateTimeEditComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
void DateTimeEditComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& element, unsigned int properties)
{
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "datetime");
if(!elem)
if (!elem)
return;
// We set mAutoSize BEFORE calling GuiComponent::applyTheme because it calls
// setSize(), which will call updateTextCache(), which will reset mSize if
// mAutoSize == true, ignoring the theme's value.
if(properties & ThemeFlags::SIZE)
if (properties & ThemeFlags::SIZE)
mAutoSize = !elem->has("size");
GuiComponent::applyTheme(theme, view, element, properties);
using namespace ThemeFlags;
if(properties & COLOR && elem->has("color"))
if (properties & COLOR && elem->has("color"))
setColor(elem->get<unsigned int>("color"));
if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
setUppercase(elem->get<bool>("forceUppercase"));
setFont(Font::getFromTheme(elem, properties, mFont));

View file

@ -1,3 +1,9 @@
//
// DateTimeEditComponent.h
//
// Date and time edit component.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
#define ES_CORE_COMPONENTS_DATE_TIME_EDIT_COMPONENT_H
@ -11,8 +17,7 @@ class TextCache;
class DateTimeEditComponent : public GuiComponent
{
public:
enum DisplayMode
{
enum DisplayMode{
DISP_DATE,
DISP_DATE_TIME,
DISP_RELATIVE_TO_NOW
@ -31,15 +36,21 @@ public:
// Set how the point in time will be displayed:
// * DISP_DATE - only display the date.
// * DISP_DATE_TIME - display both the date and the time on that date.
// * DISP_RELATIVE_TO_NOW - intelligently display the point in time relative to right now (e.g. "5 secs ago", "3 minutes ago", "1 day ago". Automatically updates as time marches on.
// * DISP_RELATIVE_TO_NOW - intelligently display the point in time relative to
// right now (e.g. "5 secs ago", "3 minutes ago", "1 day ago".
// Automatically updates as time marches on.
// The initial value is DISP_DATE.
void setDisplayMode(DisplayMode mode);
void setColor(unsigned int color); // Text color.
void setFont(std::shared_ptr<Font> font); // Font to display with. Default is Font::get(FONT_SIZE_MEDIUM).
void setUppercase(bool uppercase); // Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
// Text color.
void setColor(unsigned int color);
// Font to use. Default is Font::get(FONT_SIZE_MEDIUM).
void setFont(std::shared_ptr<Font> font);
// Force text to be uppercase when in DISP_RELATIVE_TO_NOW mode.
void setUppercase(bool uppercase);
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;
private:
std::shared_ptr<Font> getFont() const;
@ -64,7 +75,6 @@ private:
unsigned int mColor;
std::shared_ptr<Font> mFont;
bool mUppercase;
bool mAutoSize;
};

View file

@ -1,3 +1,9 @@
//
// MenuComponent.cpp
//
// Basic component for building a menu.
//
#include "components/MenuComponent.h"
#include "components/ButtonComponent.h"
@ -8,22 +14,27 @@
#define TITLE_HEIGHT (mTitle->getFont()->getLetterHeight() + TITLE_VERT_PADDING)
MenuComponent::MenuComponent(Window* window, const char* title, const std::shared_ptr<Font>& titleFont) : GuiComponent(window),
mBackground(window), mGrid(window, Vector2i(1, 3))
MenuComponent::MenuComponent(
Window* window,
const char* title,
const std::shared_ptr<Font>& titleFont)
: GuiComponent(window),
mBackground(window),
mGrid(window, Vector2i(1, 3))
{
addChild(&mBackground);
addChild(&mGrid);
mBackground.setImagePath(":/frame.png");
// set up title
// Set up title.
mTitle = std::make_shared<TextComponent>(mWindow);
mTitle->setHorizontalAlignment(ALIGN_CENTER);
mTitle->setColor(0x555555FF);
setTitle(title, titleFont);
mGrid.setEntry(mTitle, Vector2i(0, 0), false);
// set up list which will never change (externally, anyway)
// Set up list which will never change (externally, anyway).
mList = std::make_shared<ComponentList>(mWindow);
mGrid.setEntry(mList, Vector2i(0, 1), true);
@ -40,10 +51,10 @@ MenuComponent::~MenuComponent()
void MenuComponent::save()
{
if(!mSaveFuncs.size())
if (!mSaveFuncs.size())
return;
for(auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++)
for (auto it = mSaveFuncs.cbegin(); it != mSaveFuncs.cend(); it++)
(*it)();
Settings::getInstance()->saveFile();
@ -57,21 +68,20 @@ void MenuComponent::setTitle(const char* title, const std::shared_ptr<Font>& fon
float MenuComponent::getButtonGridHeight() const
{
return (mButtonGrid ? mButtonGrid->getSize().y() : Font::get(FONT_SIZE_MEDIUM)->getHeight() + BUTTON_GRID_VERT_PADDING);
return (mButtonGrid ? mButtonGrid->getSize().y()
: Font::get(FONT_SIZE_MEDIUM)->getHeight() + BUTTON_GRID_VERT_PADDING);
}
void MenuComponent::updateSize()
{
const float maxHeight = Renderer::getScreenHeight() * 0.75f;
float height = TITLE_HEIGHT + mList->getTotalRowHeight() + getButtonGridHeight() + 2;
if(height > maxHeight)
{
if (height > maxHeight) {
height = TITLE_HEIGHT + getButtonGridHeight();
int i = 0;
while(i < mList->size())
{
while (i < mList->size()) {
float rowHeight = mList->getRowHeight(i);
if(height + rowHeight < maxHeight)
if (height + rowHeight < maxHeight)
height += rowHeight;
else
break;
@ -79,7 +89,8 @@ void MenuComponent::updateSize()
}
}
float width = (float)Math::min((int)Renderer::getScreenHeight(), (int)(Renderer::getScreenWidth() * 0.90f));
float width = (float)Math::min((int)Renderer::getScreenHeight(),
(int)(Renderer::getScreenWidth() * 0.90f));
setSize(width, height);
}
@ -87,29 +98,30 @@ void MenuComponent::onSizeChanged()
{
mBackground.fitTo(mSize, Vector3f::Zero(), Vector2f(-32, -32));
// update grid row/col sizes
// Update grid row/col sizes.
mGrid.setRowHeightPerc(0, TITLE_HEIGHT / mSize.y());
mGrid.setRowHeightPerc(2, getButtonGridHeight() / mSize.y());
mGrid.setSize(mSize);
}
void MenuComponent::addButton(const std::string& name, const std::string& helpText, const std::function<void()>& callback)
void MenuComponent::addButton(const std::string& name,
const std::string& helpText, const std::function<void()>& callback)
{
mButtons.push_back(std::make_shared<ButtonComponent>(mWindow, Utils::String::toUpper(name), helpText, callback));
mButtons.push_back(std::make_shared<ButtonComponent>
(mWindow,Utils::String::toUpper(name), helpText, callback));
updateGrid();
updateSize();
}
void MenuComponent::updateGrid()
{
if(mButtonGrid)
if (mButtonGrid)
mGrid.removeEntry(mButtonGrid);
mButtonGrid.reset();
if(mButtons.size())
{
if (mButtons.size()) {
mButtonGrid = makeButtonGrid(mWindow, mButtons);
mGrid.setEntry(mButtonGrid, Vector2i(0, 2), true, false);
}
@ -120,23 +132,26 @@ std::vector<HelpPrompt> MenuComponent::getHelpPrompts()
return mGrid.getHelpPrompts();
}
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window, const std::vector< std::shared_ptr<ButtonComponent> >& buttons)
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window,
const std::vector< std::shared_ptr<ButtonComponent> >& buttons)
{
std::shared_ptr<ComponentGrid> buttonGrid = std::make_shared<ComponentGrid>(window, Vector2i((int)buttons.size(), 2));
std::shared_ptr<ComponentGrid> buttonGrid = std::make_shared<ComponentGrid>
(window, Vector2i((int)buttons.size(), 2));
float buttonGridWidth = (float)BUTTON_GRID_HORIZ_PADDING * buttons.size(); // initialize to padding
for(int i = 0; i < (int)buttons.size(); i++)
{
// Initialize to padding.
float buttonGridWidth = (float)BUTTON_GRID_HORIZ_PADDING * buttons.size();
for (int i = 0; i < (int)buttons.size(); i++) {
buttonGrid->setEntry(buttons.at(i), Vector2i(i, 0), true, false);
buttonGridWidth += buttons.at(i)->getSize().x();
}
for(unsigned int i = 0; i < buttons.size(); i++)
{
buttonGrid->setColWidthPerc(i, (buttons.at(i)->getSize().x() + BUTTON_GRID_HORIZ_PADDING) / buttonGridWidth);
}
for (unsigned int i = 0; i < buttons.size(); i++)
buttonGrid->setColWidthPerc(i, (buttons.at(i)->getSize().x() +
BUTTON_GRID_HORIZ_PADDING) / buttonGridWidth);
buttonGrid->setSize(buttonGridWidth, buttons.at(0)->getSize().y() + BUTTON_GRID_VERT_PADDING + 2);
buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y()); // spacer row to deal with dropshadow to make buttons look centered
buttonGrid->setSize(buttonGridWidth, buttons.at(0)->getSize().y() +
BUTTON_GRID_VERT_PADDING + 2);
// Spacer row to deal with dropshadow to make buttons look centered.
buttonGrid->setRowHeightPerc(1, 2 / buttonGrid->getSize().y());
return buttonGrid;
}

View file

@ -1,3 +1,9 @@
//
// MenuComponent.h
//
// Basic component for building a menu.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_MENU_COMPONENT_H
#define ES_CORE_COMPONENTS_MENU_COMPONENT_H
@ -11,7 +17,8 @@
class ButtonComponent;
class ImageComponent;
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window, const std::vector< std::shared_ptr<ButtonComponent> >& buttons);
std::shared_ptr<ComponentGrid> makeButtonGrid(Window* window,
const std::vector< std::shared_ptr<ButtonComponent> >& buttons);
std::shared_ptr<ImageComponent> makeArrow(Window* window);
#define TITLE_VERT_PADDING (Renderer::getScreenHeight()*0.0637f)
@ -19,25 +26,30 @@ std::shared_ptr<ImageComponent> makeArrow(Window* window);
class MenuComponent : public GuiComponent
{
public:
MenuComponent(Window* window, const char* title, const std::shared_ptr<Font>& titleFont = Font::get(FONT_SIZE_LARGE));
MenuComponent(Window* window, const char* title,
const std::shared_ptr<Font>& titleFont = Font::get(FONT_SIZE_LARGE));
virtual ~MenuComponent(); // just calls save();
void save();
void onSizeChanged() override;
inline void addRow(const ComponentListRow& row, bool setCursorHere = false) { mList->addRow(row, setCursorHere); updateSize(); }
inline void addRow(const ComponentListRow& row, bool setCursorHere = false)
{ mList->addRow(row, setCursorHere); updateSize(); }
inline void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp, bool setCursorHere = false, bool invert_when_selected = true)
inline void addWithLabel(const std::string& label, const std::shared_ptr<GuiComponent>& comp,
bool setCursorHere = false, bool invert_when_selected = true)
{
ComponentListRow row;
row.addElement(std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(std::make_shared<TextComponent>(mWindow,
Utils::String::toUpper(label), Font::get(FONT_SIZE_MEDIUM), 0x777777FF), true);
row.addElement(comp, false, invert_when_selected);
addRow(row, setCursorHere);
}
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); };
void addButton(const std::string& label, const std::string& helpText, const std::function<void()>& callback);
void addButton(const std::string& label, const std::string& helpText,
const std::function<void()>& callback);
void setTitle(const char* title, const std::shared_ptr<Font>& font);

View file

@ -1,8 +1,19 @@
//
// SwitchComponent.cpp
//
// Basic switch used in the menus.
//
#include "SwitchComponent.h"
#include "resources/Font.h"
SwitchComponent::SwitchComponent(Window* window, bool state) : GuiComponent(window), mImage(window), mState(state)
SwitchComponent::SwitchComponent(
Window* window,
bool state)
: GuiComponent(window),
mImage(window),
mState(state)
{
mImage.setImage(":/off.svg");
mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
@ -16,8 +27,7 @@ void SwitchComponent::onSizeChanged()
bool SwitchComponent::input(InputConfig* config, Input input)
{
if(config->isMappedTo("a", input) && input.value)
{
if(config->isMappedTo("a", input) && input.value) {
mState = !mState;
onStateChanged();
return true;
@ -29,9 +39,7 @@ bool SwitchComponent::input(InputConfig* config, Input input)
void SwitchComponent::render(const Transform4x4f& parentTrans)
{
Transform4x4f trans = parentTrans * getTransform();
mImage.render(trans);
renderChildren(trans);
}
@ -54,12 +62,9 @@ std::string SwitchComponent::getValue() const
void SwitchComponent::setValue(const std::string& statestring)
{
if (statestring == "true")
{
mState = true;
}else
{
else
mState = false;
}
onStateChanged();
}

View file

@ -1,3 +1,9 @@
//
// SwitchComponent.h
//
// Basic switch used in the menus.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
#define ES_CORE_COMPONENTS_SWITCH_COMPONENT_H
@ -5,8 +11,7 @@
#include "components/ImageComponent.h"
#include "GuiComponent.h"
// A very simple "on/off" switch.
// Should hopefully be switched to use images instead of text in the future.
// A simple "on/off" switch.
class SwitchComponent : public GuiComponent
{
public:

View file

@ -1,3 +1,9 @@
//
// TextEditComponent.cpp
//
// Component for editing text fields in menus.
//
#include "components/TextEditComponent.h"
#include "resources/Font.h"
@ -7,17 +13,20 @@
#define TEXT_PADDING_VERT 2
#define CURSOR_REPEAT_START_DELAY 500
#define CURSOR_REPEAT_SPEED 28 // lower is faster
#define CURSOR_REPEAT_SPEED 28 // Lower is faster.
TextEditComponent::TextEditComponent(Window* window) : GuiComponent(window),
mBox(window, ":/textinput_ninepatch.png"), mFocused(false),
mScrollOffset(0.0f, 0.0f), mCursor(0), mEditing(false), mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)),
mCursorRepeatDir(0)
TextEditComponent::TextEditComponent(
Window* window)
: GuiComponent(window),
mBox(window, ":/textinput_ninepatch.png"),
mFocused(false),
mScrollOffset(0.0f, 0.0f),
mCursor(0), mEditing(false),
mFont(Font::get(FONT_SIZE_MEDIUM, FONT_PATH_LIGHT)),
mCursorRepeatDir(0)
{
addChild(&mBox);
onFocusLost();
setSize(4096, mFont->getHeight() + TEXT_PADDING_VERT);
}
@ -36,7 +45,7 @@ void TextEditComponent::onFocusLost()
void TextEditComponent::onSizeChanged()
{
mBox.fitTo(mSize, Vector3f::Zero(), Vector2f(-34, -32 - TEXT_PADDING_VERT));
onTextChanged(); // wrap point probably changed
onTextChanged(); // Wrap point probably changed.
}
void TextEditComponent::setValue(const std::string& val)
@ -52,18 +61,16 @@ std::string TextEditComponent::getValue() const
void TextEditComponent::textInput(const char* text)
{
if(mEditing)
{
if (mEditing) {
mCursorRepeatDir = 0;
if(text[0] == '\b')
{
if(mCursor > 0)
{
if (text[0] == '\b') {
if (mCursor > 0) {
size_t newCursor = Utils::String::prevCursor(mText, mCursor);
mText.erase(mText.begin() + newCursor, mText.begin() + mCursor);
mCursor = (unsigned int)newCursor;
}
}else{
}
else {
mText.insert(mCursor, text);
mCursor += (unsigned int)strlen(text);
}
@ -89,80 +96,73 @@ void TextEditComponent::stopEditing()
bool TextEditComponent::input(InputConfig* config, Input input)
{
bool const cursor_left = (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("left", input)) ||
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_LEFT);
bool const cursor_right = (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("right", input)) ||
(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RIGHT);
bool const cursor_left = (config->getDeviceId() != DEVICE_KEYBOARD &&
config->isMappedLike("left", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
input.id == SDLK_LEFT);
bool const cursor_right = (config->getDeviceId() != DEVICE_KEYBOARD &&
config->isMappedLike("right", input)) || (config->getDeviceId() == DEVICE_KEYBOARD &&
input.id == SDLK_RIGHT);
if(input.value == 0)
{
if(cursor_left || cursor_right)
if (input.value == 0) {
if (cursor_left || cursor_right)
mCursorRepeatDir = 0;
return false;
}
if((config->isMappedTo("a", input) || (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN)) && mFocused && !mEditing)
{
if ((config->isMappedTo("a", input) || (config->getDeviceId() == DEVICE_KEYBOARD &&
input.id == SDLK_RETURN)) && mFocused && !mEditing) {
startEditing();
return true;
}
if(mEditing)
{
if(config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN)
{
if(isMultiline())
{
if (mEditing) {
if (config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_RETURN) {
if (isMultiline())
textInput("\n");
}else{
else
stopEditing();
}
return true;
}
if((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) || (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedTo("b", input)))
{
if ((config->getDeviceId() == DEVICE_KEYBOARD && input.id == SDLK_ESCAPE) ||
(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedTo("b", input))) {
stopEditing();
return true;
}
if(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("up", input))
{
// TODO
}else if(config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("down", input))
{
// TODO
}else if(cursor_left || cursor_right)
{
if (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("up", input)) {
// TODO.
}
else if (config->getDeviceId() != DEVICE_KEYBOARD && config->isMappedLike("down", input)) {
// TODO.
}
else if (cursor_left || cursor_right) {
mCursorRepeatDir = cursor_left ? -1 : 1;
mCursorRepeatTimer = -(CURSOR_REPEAT_START_DELAY - CURSOR_REPEAT_SPEED);
moveCursor(mCursorRepeatDir);
} else if(config->getDeviceId() == DEVICE_KEYBOARD)
{
switch(input.id)
{
case SDLK_HOME:
setCursor(0);
break;
}
else if (config->getDeviceId() == DEVICE_KEYBOARD) {
switch (input.id) {
case SDLK_HOME:
setCursor(0);
break;
case SDLK_END:
setCursor(std::string::npos);
break;
case SDLK_END:
setCursor(std::string::npos);
break;
case SDLK_DELETE:
if(mCursor < mText.length())
{
// Fake as Backspace one char to the right
moveCursor(1);
textInput("\b");
}
break;
case SDLK_DELETE:
if (mCursor < mText.length()) {
// Fake as Backspace one char to the right.
moveCursor(1);
textInput("\b");
}
break;
}
}
//consume all input when editing text
// Consume all input when editing text.
return true;
}
@ -177,12 +177,11 @@ void TextEditComponent::update(int deltaTime)
void TextEditComponent::updateCursorRepeat(int deltaTime)
{
if(mCursorRepeatDir == 0)
if (mCursorRepeatDir == 0)
return;
mCursorRepeatTimer += deltaTime;
while(mCursorRepeatTimer >= CURSOR_REPEAT_SPEED)
{
while (mCursorRepeatTimer >= CURSOR_REPEAT_SPEED) {
moveCursor(mCursorRepeatDir);
mCursorRepeatTimer -= CURSOR_REPEAT_SPEED;
}
@ -196,7 +195,7 @@ void TextEditComponent::moveCursor(int amt)
void TextEditComponent::setCursor(size_t pos)
{
if(pos == std::string::npos)
if (pos == std::string::npos)
mCursor = (unsigned int)mText.length();
else
mCursor = (int)pos;
@ -206,36 +205,34 @@ void TextEditComponent::setCursor(size_t pos)
void TextEditComponent::onTextChanged()
{
std::string wrappedText = (isMultiline() ? mFont->wrapText(mText, getTextAreaSize().x()) : mText);
mTextCache = std::unique_ptr<TextCache>(mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity()));
std::string wrappedText = (isMultiline() ?
mFont->wrapText(mText, getTextAreaSize().x()) : mText);
mTextCache = std::unique_ptr<TextCache>
(mFont->buildTextCache(wrappedText, 0, 0, 0x77777700 | getOpacity()));
if(mCursor > (int)mText.length())
if (mCursor > (int)mText.length())
mCursor = (unsigned int)mText.length();
}
void TextEditComponent::onCursorChanged()
{
if(isMultiline())
{
Vector2f textSize = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
if (isMultiline()) {
Vector2f textSize = mFont->
getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
if(mScrollOffset.y() + getTextAreaSize().y() < textSize.y() + mFont->getHeight()) //need to scroll down?
{
if (mScrollOffset.y() + getTextAreaSize().y() < textSize.y() +
mFont->getHeight()) // Need to scroll down?
mScrollOffset[1] = textSize.y() - getTextAreaSize().y() + mFont->getHeight();
}else if(mScrollOffset.y() > textSize.y()) //need to scroll up?
{
else if (mScrollOffset.y() > textSize.y()) // Need to scroll up?
mScrollOffset[1] = textSize.y();
}
}else{
}
else {
Vector2f cursorPos = mFont->sizeText(mText.substr(0, mCursor));
if(mScrollOffset.x() + getTextAreaSize().x() < cursorPos.x())
{
if (mScrollOffset.x() + getTextAreaSize().x() < cursorPos.x())
mScrollOffset[0] = cursorPos.x() - getTextAreaSize().x();
}else if(mScrollOffset.x() > cursorPos.x())
{
else if (mScrollOffset.x() > cursorPos.x())
mScrollOffset[0] = cursorPos.x();
}
}
}
@ -244,40 +241,40 @@ void TextEditComponent::render(const Transform4x4f& parentTrans)
Transform4x4f trans = getTransform() * parentTrans;
renderChildren(trans);
// text + cursor rendering
// offset into our "text area" (padding)
// Text + cursor rendering.
// Offset into our "text area" (padding).
trans.translation() += Vector3f(getTextAreaPos().x(), getTextAreaPos().y(), 0);
Vector2i clipPos((int)trans.translation().x(), (int)trans.translation().y());
Vector3f dimScaled = trans * Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0); // use "text area" size for clipping
Vector2i clipDim((int)(dimScaled.x() - trans.translation().x()), (int)(dimScaled.y() - trans.translation().y()));
// Use "text area" size for clipping.
Vector3f dimScaled = trans * Vector3f(getTextAreaSize().x(), getTextAreaSize().y(), 0);
Vector2i clipDim((int)(dimScaled.x() - trans.translation().x()), (int)(dimScaled.y() -
trans.translation().y()));
Renderer::pushClipRect(clipPos, clipDim);
trans.translate(Vector3f(-mScrollOffset.x(), -mScrollOffset.y(), 0));
Renderer::setMatrix(trans);
if(mTextCache)
{
if (mTextCache)
mFont->renderTextCache(mTextCache.get());
}
// pop the clip early to allow the cursor to be drawn outside of the "text area"
// Pop the clip early to allow the cursor to be drawn outside of the "text area".
Renderer::popClipRect();
// draw cursor
if(mEditing)
{
// Draw cursor.
if (mEditing) {
Vector2f cursorPos;
if(isMultiline())
{
if (isMultiline()) {
cursorPos = mFont->getWrappedTextCursorOffset(mText, getTextAreaSize().x(), mCursor);
}else{
}
else {
cursorPos = mFont->sizeText(mText.substr(0, mCursor));
cursorPos[1] = 0;
}
float cursorHeight = mFont->getHeight() * 0.8f;
Renderer::drawRect(cursorPos.x(), cursorPos.y() + (mFont->getHeight() - cursorHeight) / 2, 2.0f, cursorHeight, 0x000000FF, 0x000000FF);
Renderer::drawRect(cursorPos.x(), cursorPos.y() + (mFont->getHeight() -
cursorHeight) / 2, 2.0f, cursorHeight, 0x000000FF, 0x000000FF);
}
}
@ -299,11 +296,11 @@ Vector2f TextEditComponent::getTextAreaSize() const
std::vector<HelpPrompt> TextEditComponent::getHelpPrompts()
{
std::vector<HelpPrompt> prompts;
if(mEditing)
{
if (mEditing) {
prompts.push_back(HelpPrompt("up/down/left/right", "move cursor"));
prompts.push_back(HelpPrompt("b", "stop editing"));
}else{
}
else {
prompts.push_back(HelpPrompt("a", "edit"));
}
return prompts;

View file

@ -1,3 +1,9 @@
//
// TextEditComponent.h
//
// Component for editing text fields in menus.
//
#pragma once
#ifndef ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
#define ES_CORE_COMPONENTS_TEXT_EDIT_COMPONENT_H
@ -51,7 +57,7 @@ private:
std::string mText;
bool mFocused;
bool mEditing;
unsigned int mCursor; // cursor position in characters
unsigned int mCursor; // Cursor position in characters.
int mCursorRepeatTimer;
int mCursorRepeatDir;

View file

@ -1,3 +1,9 @@
//
// TextListComponent.h
//
// Used for displaying and navigating the gamelists.
//
#pragma once
#ifndef ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
#define ES_APP_COMPONENTS_TEXT_LIST_COMPONENT_H
@ -11,13 +17,12 @@
class TextCache;
struct TextListData
{
struct TextListData {
unsigned int colorId;
std::shared_ptr<TextCache> textCache;
};
//A graphical list. Supports multiple colors for rows and scrolling.
// A graphical list. Supports multiple colors for rows and scrolling.
template <typename T>
class TextListComponent : public IList<TextListData, T>
{
@ -41,12 +46,12 @@ public:
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
void render(const Transform4x4f& parentTrans) override;
void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override;
void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
const std::string& element, unsigned int properties) override;
void add(const std::string& name, const T& obj, unsigned int colorId);
enum Alignment
{
enum Alignment {
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT
@ -54,19 +59,20 @@ public:
inline void setAlignment(Alignment align) { mAlignment = align; }
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func) { mCursorChangedCallback = func; }
inline void setCursorChangedCallback(const std::function<void(CursorState state)>& func)
{ mCursorChangedCallback = func; }
inline void setFont(const std::shared_ptr<Font>& font)
{
mFont = font;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
it->data.textCache.reset();
}
inline void setUppercase(bool /*uppercase*/)
{
mUppercase = true;
for(auto it = mEntries.begin(); it != mEntries.end(); it++)
for (auto it = mEntries.begin(); it != mEntries.end(); it++)
it->data.textCache.reset();
}
@ -74,7 +80,8 @@ public:
inline void setSelectorOffsetY(float selectorOffsetY) { mSelectorOffsetY = selectorOffsetY; }
inline void setSelectorColor(unsigned int color) { mSelectorColor = color; }
inline void setSelectorColorEnd(unsigned int color) { mSelectorColorEnd = color; }
inline void setSelectorColorGradientHorizontal(bool horizontal) { mSelectorColorGradientHorizontal = horizontal; }
inline void setSelectorColorGradientHorizontal(bool horizontal)
{ mSelectorColorGradientHorizontal = horizontal; }
inline void setSelectedColor(unsigned int color) { mSelectedColor = color; }
inline void setColor(unsigned int id, unsigned int color) { mColors[id] = color; }
inline void setLineSpacing(float lineSpacing) { mLineSpacing = lineSpacing; }
@ -141,90 +148,99 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
std::shared_ptr<Font>& font = mFont;
if(size() == 0)
if (size() == 0)
return;
const float entrySize = Math::max(font->getHeight(1.0), (float)font->getSize()) * mLineSpacing;
int startEntry = 0;
//number of entries that can fit on the screen simultaniously
// Number of entries that can fit on the screen simultaniously.
int screenCount = (int)(mSize.y() / entrySize + 0.5f);
if(size() >= screenCount)
if (size() >= screenCount)
{
startEntry = mCursor - screenCount/2;
if(startEntry < 0)
if (startEntry < 0)
startEntry = 0;
if(startEntry >= size() - screenCount)
if (startEntry >= size() - screenCount)
startEntry = size() - screenCount;
}
float y = 0;
int listCutoff = startEntry + screenCount;
if(listCutoff > size())
if (listCutoff > size())
listCutoff = size();
// draw selector bar
if(startEntry < listCutoff)
{
// Draw selector bar.
if (startEntry < listCutoff) {
if (mSelectorImage.hasImage()) {
mSelectorImage.setPosition(0.f, (mCursor - startEntry)*entrySize + mSelectorOffsetY, 0.f);
mSelectorImage.setPosition(0.f,
(mCursor - startEntry)*entrySize + mSelectorOffsetY, 0.f);
mSelectorImage.render(trans);
} else {
}
else {
Renderer::setMatrix(trans);
Renderer::drawRect(0.0f, (mCursor - startEntry)*entrySize + mSelectorOffsetY, mSize.x(),
mSelectorHeight, mSelectorColor, mSelectorColorEnd, mSelectorColorGradientHorizontal);
Renderer::drawRect(
0.0f,
(mCursor - startEntry)*entrySize +
mSelectorOffsetY,
mSize.x(),
mSelectorHeight,
mSelectorColor,
mSelectorColorEnd,
mSelectorColorGradientHorizontal);
}
}
// clip to inside margins
// Clip to inside margins.
Vector3f dim(mSize.x(), mSize.y(), 0);
dim = trans * dim - trans.translation();
Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin), (int)trans.translation().y()),
Vector2i((int)(dim.x() - mHorizontalMargin*2), (int)dim.y()));
Renderer::pushClipRect(Vector2i((int)(trans.translation().x() + mHorizontalMargin),
(int)trans.translation().y()), Vector2i((int)(dim.x() - mHorizontalMargin*2),
(int)dim.y()));
for(int i = startEntry; i < listCutoff; i++)
{
for (int i = startEntry; i < listCutoff; i++) {
typename IList<TextListData, T>::Entry& entry = mEntries.at((unsigned int)i);
unsigned int color;
if(mCursor == i && mSelectedColor)
if (mCursor == i && mSelectedColor)
color = mSelectedColor;
else
color = mColors[entry.data.colorId];
if(!entry.data.textCache)
entry.data.textCache = std::unique_ptr<TextCache>(font->buildTextCache(mUppercase ? Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
if (!entry.data.textCache)
entry.data.textCache = std::unique_ptr<TextCache>
(font->buildTextCache(mUppercase ?
Utils::String::toUpper(entry.name) : entry.name, 0, 0, 0x000000FF));
entry.data.textCache->setColor(color);
Vector3f offset(0, y, 0);
switch(mAlignment)
{
switch (mAlignment) {
case ALIGN_LEFT:
offset[0] = mHorizontalMargin;
break;
case ALIGN_CENTER:
offset[0] = (int)((mSize.x() - entry.data.textCache->metrics.size.x()) / 2);
if(offset[0] < mHorizontalMargin)
if (offset[0] < mHorizontalMargin)
offset[0] = mHorizontalMargin;
break;
case ALIGN_RIGHT:
offset[0] = (mSize.x() - entry.data.textCache->metrics.size.x());
offset[0] -= mHorizontalMargin;
if(offset[0] < mHorizontalMargin)
if (offset[0] < mHorizontalMargin)
offset[0] = mHorizontalMargin;
break;
}
// render text
// Render text.
Transform4x4f drawTrans = trans;
// currently selected item text might be scrolling
if((mCursor == i) && (mMarqueeOffset > 0))
// Currently selected item text might be scrolling.
if ((mCursor == i) && (mMarqueeOffset > 0))
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset, 0, 0));
else
drawTrans.translate(offset);
@ -232,10 +248,9 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
Renderer::setMatrix(drawTrans);
font->renderTextCache(entry.data.textCache.get());
// render currently selected item text again if
// marquee is scrolled far enough for it to repeat
if((mCursor == i) && (mMarqueeOffset2 < 0))
{
// Render currently selected item text again if marquee is
// scrolled far enough for it to repeat.
if ((mCursor == i) && (mMarqueeOffset2 < 0)) {
drawTrans = trans;
drawTrans.translate(offset - Vector3f((float)mMarqueeOffset2, 0, 0));
Renderer::setMatrix(drawTrans);
@ -246,47 +261,40 @@ void TextListComponent<T>::render(const Transform4x4f& parentTrans)
}
Renderer::popClipRect();
listRenderTitleOverlay(trans);
GuiComponent::renderChildren(trans);
}
template <typename T>
bool TextListComponent<T>::input(InputConfig* config, Input input)
{
if(size() > 0)
{
if(input.value != 0)
{
if(config->isMappedLike("down", input))
{
if (size() > 0) {
if (input.value != 0) {
if (config->isMappedLike("down", input)) {
listInput(1);
return true;
}
if(config->isMappedLike("up", input))
{
if (config->isMappedLike("up", input)) {
listInput(-1);
return true;
}
if(config->isMappedLike("rightshoulder", input))
{
if (config->isMappedLike("rightshoulder", input)) {
listInput(10);
return true;
}
if(config->isMappedLike("leftshoulder", input))
{
if (config->isMappedLike("leftshoulder", input)) {
listInput(-10);
return true;
}
}else{
if(config->isMappedLike("down", input) || config->isMappedLike("up", input) ||
config->isMappedLike("rightshoulder", input) || config->isMappedLike("leftshoulder", input))
{
}
else {
if (config->isMappedLike("down", input) ||
config->isMappedLike("up", input) ||
config->isMappedLike("rightshoulder", input) ||
config->isMappedLike("leftshoulder", input))
stopScrolling();
}
}
}
@ -298,20 +306,18 @@ void TextListComponent<T>::update(int deltaTime)
{
listUpdate(deltaTime);
if(!isScrolling() && size() > 0)
{
// always reset the marquee offsets
if (!isScrolling() && size() > 0) {
// Always reset the marquee offsets.
mMarqueeOffset = 0;
mMarqueeOffset2 = 0;
// if we're not scrolling and this object's text goes outside our size, marquee it!
// If we're not scrolling and this object's text goes outside our size, marquee it!
const float textLength = mFont->sizeText(mEntries.at((unsigned int)mCursor).name).x();
const float limit = mSize.x() - mHorizontalMargin * 2;
if(textLength > limit)
{
// loop
// pixels per second ( based on nes-mini font at 1920x1080 to produce a speed of 200 )
if (textLength > limit) {
// Loop.
// Pixels per second (based on nes-mini font at 1920x1080 to produce a speed of 200).
const float speed = mFont->sizeText("ABCDEFGHIJKLMNOPQRSTUVWXYZ").x() * 0.247f;
const float delay = 3000;
const float scrollLength = textLength;
@ -321,12 +327,13 @@ void TextListComponent<T>::update(int deltaTime)
const int maxTime = (int)(delay + scrollTime + returnTime);
mMarqueeTime += deltaTime;
while(mMarqueeTime > maxTime)
while (mMarqueeTime > maxTime)
mMarqueeTime -= maxTime;
mMarqueeOffset = (int)(Math::Scroll::loop(delay, scrollTime + returnTime, (float)mMarqueeTime, scrollLength + returnLength));
mMarqueeOffset = (int)(Math::Scroll::loop(delay, scrollTime + returnTime,
(float)mMarqueeTime, scrollLength + returnLength));
if(mMarqueeOffset > (scrollLength - (limit - returnLength)))
if (mMarqueeOffset > (scrollLength - (limit - returnLength)))
mMarqueeOffset2 = (int)(mMarqueeOffset - (scrollLength + returnLength));
}
}
@ -334,7 +341,7 @@ void TextListComponent<T>::update(int deltaTime)
GuiComponent::update(deltaTime);
}
//list management stuff
// List management stuff.
template <typename T>
void TextListComponent<T>::add(const std::string& name, const T& obj, unsigned int color)
{
@ -354,92 +361,89 @@ void TextListComponent<T>::onCursorChanged(const CursorState& state)
mMarqueeOffset2 = 0;
mMarqueeTime = 0;
if(mCursorChangedCallback)
if (mCursorChangedCallback)
mCursorChangedCallback(state);
}
template <typename T>
void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
void TextListComponent<T>::applyTheme(const std::shared_ptr<ThemeData>& theme,
const std::string& view, const std::string& element, unsigned int properties)
{
GuiComponent::applyTheme(theme, view, element, properties);
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "textlist");
if(!elem)
if (!elem)
return;
using namespace ThemeFlags;
if(properties & COLOR)
{
if(elem->has("selectorColor"))
{
if (properties & COLOR) {
if (elem->has("selectorColor")) {
setSelectorColor(elem->get<unsigned int>("selectorColor"));
setSelectorColorEnd(elem->get<unsigned int>("selectorColor"));
}
if (elem->has("selectorColorEnd"))
setSelectorColorEnd(elem->get<unsigned int>("selectorColorEnd"));
if (elem->has("selectorGradientType"))
setSelectorColorGradientHorizontal(!(elem->get<std::string>("selectorGradientType").compare("horizontal")));
if(elem->has("selectedColor"))
setSelectorColorGradientHorizontal(!(elem->get<std::string>
("selectorGradientType").compare("horizontal")));
if (elem->has("selectedColor"))
setSelectedColor(elem->get<unsigned int>("selectedColor"));
if(elem->has("primaryColor"))
if (elem->has("primaryColor"))
setColor(0, elem->get<unsigned int>("primaryColor"));
if(elem->has("secondaryColor"))
if (elem->has("secondaryColor"))
setColor(1, elem->get<unsigned int>("secondaryColor"));
}
setFont(Font::getFromTheme(elem, properties, mFont));
const float selectorHeight = Math::max(mFont->getHeight(1.0), (float)mFont->getSize()) * mLineSpacing;
const float selectorHeight = Math::max(mFont->getHeight(1.0),
(float)mFont->getSize()) * mLineSpacing;
setSelectorHeight(selectorHeight);
if(properties & ALIGNMENT)
{
if(elem->has("alignment"))
{
if (properties & ALIGNMENT) {
if (elem->has("alignment")) {
const std::string& str = elem->get<std::string>("alignment");
if(str == "left")
if (str == "left")
setAlignment(ALIGN_LEFT);
else if(str == "center")
else if (str == "center")
setAlignment(ALIGN_CENTER);
else if(str == "right")
else if (str == "right")
setAlignment(ALIGN_RIGHT);
else
LOG(LogError) << "Unknown TextListComponent alignment \"" << str << "\"!";
}
if(elem->has("horizontalMargin"))
{
mHorizontalMargin = elem->get<float>("horizontalMargin") * (this->mParent ? this->mParent->getSize().x() : (float)Renderer::getScreenWidth());
if (elem->has("horizontalMargin")) {
mHorizontalMargin = elem->get<float>("horizontalMargin") *
(this->mParent ? this->mParent->getSize().x() :
(float)Renderer::getScreenWidth());
}
}
if(properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
if (properties & FORCE_UPPERCASE && elem->has("forceUppercase"))
setUppercase(elem->get<bool>("forceUppercase"));
if(properties & LINE_SPACING)
{
if(elem->has("lineSpacing"))
if (properties & LINE_SPACING) {
if (elem->has("lineSpacing"))
setLineSpacing(elem->get<float>("lineSpacing"));
if(elem->has("selectorHeight"))
{
if (elem->has("selectorHeight"))
setSelectorHeight(elem->get<float>("selectorHeight") * Renderer::getScreenHeight());
}
if(elem->has("selectorOffsetY"))
{
if (elem->has("selectorOffsetY")) {
float scale = this->mParent ? this->mParent->getSize().y() : (float)Renderer::getScreenHeight();
setSelectorOffsetY(elem->get<float>("selectorOffsetY") * scale);
} else {
}
else {
setSelectorOffsetY(0.0);
}
}
if (elem->has("selectorImagePath"))
{
if (elem->has("selectorImagePath")) {
std::string path = elem->get<std::string>("selectorImagePath");
bool tile = elem->has("selectorImageTile") && elem->get<bool>("selectorImageTile");
mSelectorImage.setImage(path, tile);
mSelectorImage.setSize(mSize.x(), mSelectorHeight);
mSelectorImage.setColorShift(mSelectorColor);
mSelectorImage.setColorShiftEnd(mSelectorColorEnd);
} else {
}
else {
mSelectorImage.setImage("");
}
}