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. // Improved and extended by the RetroPie community.
// Desktop Edition fork by Leon Styhre. // 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 // main.cpp
// //

View file

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

View file

@ -1,3 +1,9 @@
//
// SystemView.h
//
// Main system view.
//
#pragma once #pragma once
#ifndef ES_APP_VIEWS_SYSTEM_VIEW_H #ifndef ES_APP_VIEWS_SYSTEM_VIEW_H
#define ES_APP_VIEWS_SYSTEM_VIEW_H #define ES_APP_VIEWS_SYSTEM_VIEW_H
@ -11,22 +17,19 @@
class AnimatedImageComponent; class AnimatedImageComponent;
class SystemData; class SystemData;
enum CarouselType : unsigned int enum CarouselType : unsigned int {
{
HORIZONTAL = 0, HORIZONTAL = 0,
VERTICAL = 1, VERTICAL = 1,
VERTICAL_WHEEL = 2, VERTICAL_WHEEL = 2,
HORIZONTAL_WHEEL = 3 HORIZONTAL_WHEEL = 3
}; };
struct SystemViewData struct SystemViewData {
{
std::shared_ptr<GuiComponent> logo; std::shared_ptr<GuiComponent> logo;
std::vector<GuiComponent*> backgroundExtras; std::vector<GuiComponent*> backgroundExtras;
}; };
struct SystemViewCarousel struct SystemViewCarousel {
{
CarouselType type; CarouselType type;
Vector2f pos; Vector2f pos;
Vector2f size; Vector2f size;
@ -38,7 +41,7 @@ struct SystemViewCarousel
unsigned int color; unsigned int color;
unsigned int colorEnd; unsigned int colorEnd;
bool colorGradientHorizontal; bool colorGradientHorizontal;
int maxLogoCount; // number of logos shown on the carousel int maxLogoCount; // Number of logos shown on the carousel.
Vector2f logoSize; Vector2f logoSize;
float zIndex; float zIndex;
}; };
@ -79,7 +82,7 @@ private:
SystemViewCarousel mCarousel; SystemViewCarousel mCarousel;
TextComponent mSystemInfo; TextComponent mSystemInfo;
// unit is list index // Unit is list index.
float mCamOffset; float mCamOffset;
float mExtrasCamOffset; float mExtrasCamOffset;
float mExtrasFadeOpacity; 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 "UIModeController.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
@ -25,8 +32,8 @@ UIModeController::UIModeController()
void UIModeController::monitorUIMode() void UIModeController::monitorUIMode()
{ {
std::string uimode = Settings::getInstance()->getString("UIMode"); std::string uimode = Settings::getInstance()->getString("UIMode");
if (uimode != mCurrentUIMode) // UIMODE HAS CHANGED // UI mode was changed.
{ if (uimode != mCurrentUIMode) {
mCurrentUIMode = uimode; mCurrentUIMode = uimode;
ViewController::get()->ReloadAndGoToStart(); ViewController::get()->ReloadAndGoToStart();
} }
@ -34,26 +41,18 @@ void UIModeController::monitorUIMode()
bool UIModeController::listen(InputConfig * config, Input input) bool UIModeController::listen(InputConfig * config, Input input)
{ {
// Reads the current input to listen for the passkey // Reads the current input to listen for the passkey sequence to unlock
// sequence to unlock the UI mode. The progress is saved in mPassKeyCounter // the UI mode. The progress is saved in mPassKeyCounter.
if (Settings::getInstance()->getBool("Debug")) if (Settings::getInstance()->getBool("Debug"))
{
logInput(config, input); logInput(config, input);
}
if ((Settings::getInstance()->getString("UIMode") == "Full") || !isValidInput(config, input)) if ((Settings::getInstance()->getString("UIMode") == "Full") || !isValidInput(config, input))
{
return false; // Already unlocked, or invalid input, nothing to do here. return false; // Already unlocked, or invalid input, nothing to do here.
}
if (!inputIsMatch(config, input)) 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(); unlockUIMode();
return true; return true;
} }
@ -62,11 +61,9 @@ bool UIModeController::listen(InputConfig * config, Input input)
bool UIModeController::inputIsMatch(InputConfig * config, Input input) bool UIModeController::inputIsMatch(InputConfig * config, Input input)
{ {
for (auto valstring : mInputVals) for (auto valstring : mInputVals) {
{
if (config->isMappedLike(valstring, input) && if (config->isMappedLike(valstring, input) &&
(mPassKeySequence[mPassKeyCounter] == valstring[0])) (mPassKeySequence[mPassKeyCounter] == valstring[0])) {
{
mPassKeyCounter++; mPassKeyCounter++;
return true; return true;
} }
@ -74,10 +71,11 @@ bool UIModeController::inputIsMatch(InputConfig * config, Input input)
return false; 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() 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()->setString("UIMode", "Full");
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
mPassKeyCounter = 0; mPassKeyCounter = 0;
@ -102,26 +100,24 @@ bool UIModeController::isUIModeKiosk()
std::string UIModeController::getFormattedPassKeyStr() 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 = ""; std::string out = "";
for (auto c : mPassKeySequence) for (auto c : mPassKeySequence) {
{ out += (out == "") ? "" : ", "; // Add a comma after the first entry.
out += (out == "") ? "" : ", "; // add a comma after the first entry
switch (c) switch (c) {
{
case 'u': case 'u':
out += Utils::String::unicode2Chars(0x2191); // arrow pointing up out += Utils::String::unicode2Chars(0x2191); // Arrow pointing up.
break; break;
case 'd': case 'd':
out += Utils::String::unicode2Chars(0x2193); // arrow pointing down out += Utils::String::unicode2Chars(0x2193); // Arrow pointing down.
break; break;
case 'l': case 'l':
out += Utils::String::unicode2Chars(0x2190); // arrow pointing left out += Utils::String::unicode2Chars(0x2190); // Arrow pointing left.
break; break;
case 'r': case 'r':
out += Utils::String::unicode2Chars(0x2192); // arrow pointing right out += Utils::String::unicode2Chars(0x2192); // Arrow pointing right.
break; break;
case 'a': case 'a':
out += "A"; out += "A";
@ -140,29 +136,26 @@ std::string UIModeController::getFormattedPassKeyStr()
return out; return out;
} }
void UIModeController::logInput(InputConfig * config, Input input) void UIModeController::logInput(InputConfig * config, Input input)
{ {
std::string mapname = ""; std::string mapname = "";
std::vector<std::string> maps = config->getMappedTo(input); std::vector<std::string> maps = config->getMappedTo(input);
for( auto mn : maps)
{ for (auto mn : maps) {
mapname += mn; mapname += mn;
mapname += ", "; 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) bool UIModeController::isValidInput(InputConfig * config, Input input)
{ {
if((config->getMappedTo(input).size() == 0) || // not a mapped input, so ignore. if ((config->getMappedTo(input).size() == 0) || // Not a mapped input, so ignore..
(input.type == TYPE_HAT) || // ignore all HAT inputs (input.type == TYPE_HAT) || // Ignore all hat inputs.
(!input.value)) // not a key-down event (!input.value)) // Not a key-down event.
{
return false; return false;
}
else else
{
return true; 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 #pragma once
#ifndef ES_APP_VIEWS_UI_MODE_CONTROLLER_H #ifndef ES_APP_VIEWS_UI_MODE_CONTROLLER_H
#define ES_APP_VIEWS_UI_MODE_CONTROLLER_H #define ES_APP_VIEWS_UI_MODE_CONTROLLER_H
@ -11,11 +18,13 @@ class ViewController;
struct Input; struct Input;
class UIModeController { class UIModeController
{
public: public:
static UIModeController* getInstance(); 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); bool listen(InputConfig* config, Input input);
// Get the current Passphrase as a (unicode) formatted, comma-separated, string. // Get the current Passphrase as a (unicode) formatted, comma-separated, string.
@ -28,22 +37,24 @@ public:
bool isUIModeKid(); bool isUIModeKid();
bool isUIModeKiosk(); bool isUIModeKiosk();
inline std::vector<std::string> getUIModes() { return mUIModes; }; inline std::vector<std::string> getUIModes() { return mUIModes; };
private: private:
UIModeController(); UIModeController();
bool inputIsMatch(InputConfig * config, Input input); bool inputIsMatch(InputConfig * config, Input input);
bool isValidInput(InputConfig * config, Input input); bool isValidInput(InputConfig * config, Input input);
void logInput(InputConfig * config, Input input); void logInput(InputConfig * config, Input input);
// Return UI mode to 'FULL' // Return UI mode to 'full'.
void unlockUIMode(); void unlockUIMode();
static UIModeController * sInstance; static UIModeController * sInstance;
const std::vector<std::string> mUIModes = { "Full", "Kiosk", "Kid" }; 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; std::string mPassKeySequence;
int mPassKeyCounter; 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; 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 "components/ComponentGrid.h"
#include "Settings.h" #include "Settings.h"
using namespace GridFlags; using namespace GridFlags;
ComponentGrid::ComponentGrid(Window* window, const Vector2i& gridDimensions) : GuiComponent(window), ComponentGrid::ComponentGrid(
mGridSize(gridDimensions), mCursor(0, 0) Window* window,
const Vector2i& gridDimensions)
: GuiComponent(window),
mGridSize(gridDimensions),
mCursor(0, 0)
{ {
assert(gridDimensions.x() > 0 && gridDimensions.y() > 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()]; mColWidths = new float[gridDimensions.x()];
mRowHeights = new float[gridDimensions.y()]; mRowHeights = new float[gridDimensions.y()];
for(int x = 0; x < gridDimensions.x(); x++) for (int x = 0; x < gridDimensions.x(); x++)
mColWidths[x] = 0; mColWidths[x] = 0;
for(int y = 0; y < gridDimensions.y(); y++) for (int y = 0; y < gridDimensions.y(); y++)
mRowHeights[y] = 0; mRowHeights[y] = 0;
} }
@ -27,16 +37,15 @@ ComponentGrid::~ComponentGrid()
float ComponentGrid::getColWidth(int col) float ComponentGrid::getColWidth(int col)
{ {
if(mColWidths[col] != 0) if (mColWidths[col] != 0)
return mColWidths[col] * mSize.x(); return mColWidths[col] * mSize.x();
// calculate automatic width // Calculate automatic width.
float freeWidthPerc = 1; float freeWidthPerc = 1;
int between = 0; int between = 0;
for(int x = 0; x < mGridSize.x(); x++) for (int x = 0; x < mGridSize.x(); x++) {
{ freeWidthPerc -= mColWidths[x]; // If it's 0 it won't do anything.
freeWidthPerc -= mColWidths[x]; // if it's 0 it won't do anything if (mColWidths[x] == 0)
if(mColWidths[x] == 0)
between++; between++;
} }
@ -45,16 +54,15 @@ float ComponentGrid::getColWidth(int col)
float ComponentGrid::getRowHeight(int row) float ComponentGrid::getRowHeight(int row)
{ {
if(mRowHeights[row] != 0) if (mRowHeights[row] != 0)
return mRowHeights[row] * mSize.y(); return mRowHeights[row] * mSize.y();
// calculate automatic height // Calculate automatic height.
float freeHeightPerc = 1; float freeHeightPerc = 1;
int between = 0; int between = 0;
for(int y = 0; y < mGridSize.y(); y++) for (int y = 0; y < mGridSize.y(); y++) {
{ freeHeightPerc -= mRowHeights[y]; // If it's 0 it won't do anything.
freeHeightPerc -= mRowHeights[y]; // if it's 0 it won't do anything if (mRowHeights[y] == 0)
if(mRowHeights[y] == 0)
between++; between++;
} }
@ -67,7 +75,7 @@ void ComponentGrid::setColWidthPerc(int col, float width, bool update)
assert(col >= 0 && col < mGridSize.x()); assert(col >= 0 && col < mGridSize.x());
mColWidths[col] = width; mColWidths[col] = width;
if(update) if (update)
onSizeChanged(); onSizeChanged();
} }
@ -77,12 +85,18 @@ void ComponentGrid::setRowHeightPerc(int row, float height, bool update)
assert(row >= 0 && row < mGridSize.y()); assert(row >= 0 && row < mGridSize.y());
mRowHeights[row] = height; mRowHeights[row] = height;
if(update) if (update)
onSizeChanged(); onSizeChanged();
} }
void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Vector2i& pos, bool canFocus, bool resize, const Vector2i& size, void ComponentGrid::setEntry(
unsigned int border, GridFlags::UpdateType updateType) 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(pos.x() >= 0 && pos.x() < mGridSize.x() && pos.y() >= 0 && pos.y() < mGridSize.y());
assert(comp != nullptr); assert(comp != nullptr);
@ -93,8 +107,7 @@ void ComponentGrid::setEntry(const std::shared_ptr<GuiComponent>& comp, const Ve
addChild(comp.get()); addChild(comp.get());
if(!cursorValid() && canFocus) if (!cursorValid() && canFocus) {
{
auto origCursor = mCursor; auto origCursor = mCursor;
mCursor = pos; mCursor = pos;
onCursorMoved(origCursor, mCursor); 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) bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
{ {
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
{ if (it->component == comp) {
if(it->component == comp)
{
removeChild(comp.get()); removeChild(comp.get());
mCells.erase(it); mCells.erase(it);
return true; return true;
@ -121,25 +132,25 @@ bool ComponentGrid::removeEntry(const std::shared_ptr<GuiComponent>& comp)
void ComponentGrid::updateCellComponent(const GridEntry& cell) void ComponentGrid::updateCellComponent(const GridEntry& cell)
{ {
// size // Size.
Vector2f size(0, 0); 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); 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); size[1] += getRowHeight(y);
if(cell.resize) if (cell.resize)
cell.component->setSize(size); cell.component->setSize(size);
// position // Position.
// find top left corner // Find top left corner.
Vector3f pos(0, 0, 0); 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); 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); pos[1] += getRowHeight(y);
// center component // Center component.
pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2; pos[0] = pos.x() + (size.x() - cell.component->getSize().x()) / 2;
pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2; pos[1] = pos.y() + (size.y() - cell.component->getSize().y()) / 2;
@ -155,49 +166,44 @@ void ComponentGrid::updateSeparators()
Vector2f pos; Vector2f pos;
Vector2f size; Vector2f size;
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
{ if (!it->border && !drawAll)
if(!it->border && !drawAll)
continue; continue;
// find component position + size // Find component position + size.
pos = Vector2f(0, 0); pos = Vector2f(0, 0);
size = 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); 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); 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); 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); size[1] += getRowHeight(y);
if(it->border & BORDER_TOP || drawAll) if (it->border & BORDER_TOP || drawAll) {
{ mLines.push_back( { { pos.x(), pos.y() }, { 0.0f, 0.0f }, color } );
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 } );
mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
} }
if(it->border & BORDER_BOTTOM || drawAll) 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(), 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 } );
mLines.push_back( { { pos.x() + size.x(), mLines.back().pos.y() }, { 0.0f, 0.0f }, color } );
} }
if(it->border & BORDER_LEFT || drawAll) 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() }, { 0.0f, 0.0f }, color } ); mLines.push_back( { { pos.x(), pos.y() + size.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) if (it->border & BORDER_RIGHT || drawAll) {
{ mLines.push_back( { { pos.x() + size.x(), pos.y() }, { 0.0f, 0.0f }, color } );
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 } );
mLines.push_back( { { mLines.back().pos.x(), pos.y() + size.y() }, { 0.0f, 0.0f }, color } );
} }
} }
} }
void ComponentGrid::onSizeChanged() void ComponentGrid::onSizeChanged()
{ {
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++)
updateCellComponent(*it); updateCellComponent(*it);
updateSeparators(); 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()); 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 xmin = it->pos.x();
int xmax = xmin + it->dim.x(); int xmax = xmin + it->dim.x();
int ymin = it->pos.y(); int ymin = it->pos.y();
int ymax = ymin + it->dim.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); return &(*it);
} }
@ -224,41 +229,34 @@ const ComponentGrid::GridEntry* ComponentGrid::getCellAt(int x, int y) const
bool ComponentGrid::input(InputConfig* config, Input input) bool ComponentGrid::input(InputConfig* config, Input input)
{ {
const GridEntry* cursorEntry = getCellAt(mCursor); const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->component->input(config, input)) if (cursorEntry && cursorEntry->component->input(config, input))
return true; return true;
if(!input.value) if (!input.value)
return false; return false;
if(config->isMappedLike("down", input)) if (config->isMappedLike("down", input))
{
return moveCursor(Vector2i(0, 1)); return moveCursor(Vector2i(0, 1));
}
if(config->isMappedLike("up", input)) if (config->isMappedLike("up", input))
{
return moveCursor(Vector2i(0, -1)); return moveCursor(Vector2i(0, -1));
}
if(config->isMappedLike("left", input)) if (config->isMappedLike("left", input))
{
return moveCursor(Vector2i(-1, 0)); return moveCursor(Vector2i(-1, 0));
}
if(config->isMappedLike("right", input)) if (config->isMappedLike("right", input))
{
return moveCursor(Vector2i(1, 0)); return moveCursor(Vector2i(1, 0));
}
return false; return false;
} }
void ComponentGrid::resetCursor() void ComponentGrid::resetCursor()
{ {
if(!mCells.size()) if (!mCells.size())
return; return;
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
{ if (it->canFocus) {
if(it->canFocus)
{
Vector2i origCursor = mCursor; Vector2i origCursor = mCursor;
mCursor = it->pos; mCursor = it->pos;
onCursorMoved(origCursor, mCursor); onCursorMoved(origCursor, mCursor);
@ -272,51 +270,42 @@ bool ComponentGrid::moveCursor(Vector2i dir)
assert(dir.x() || dir.y()); assert(dir.x() || dir.y());
const Vector2i origCursor = mCursor; const Vector2i origCursor = mCursor;
const GridEntry* currentCursorEntry = getCellAt(mCursor); const GridEntry* currentCursorEntry = getCellAt(mCursor);
Vector2i searchAxis(dir.x() == 0, dir.y() == 0); Vector2i searchAxis(dir.x() == 0, dir.y() == 0);
while(mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) while (mCursor.x() >= 0 && mCursor.y() >= 0 && mCursor.x() < mGridSize.x() &&
{ mCursor.y() < mGridSize.y()) {
mCursor = mCursor + dir; mCursor = mCursor + dir;
Vector2i curDirPos = mCursor; Vector2i curDirPos = mCursor;
const GridEntry* cursorEntry; const GridEntry* cursorEntry;
//spread out on search axis+
while(mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y() // Spread out on search axis+
&& mCursor.x() >= 0 && mCursor.y() >= 0) while (mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()
{ && mCursor.x() >= 0 && mCursor.y() >= 0) {
cursorEntry = getCellAt(mCursor); cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
{
onCursorMoved(origCursor, mCursor); onCursorMoved(origCursor, mCursor);
return true; return true;
} }
mCursor += searchAxis; mCursor += searchAxis;
} }
//now again on search axis- // Now again on search axis-
mCursor = curDirPos; mCursor = curDirPos;
while(mCursor.x() >= 0 && mCursor.y() >= 0 while (mCursor.x() >= 0 && mCursor.y() >= 0
&& mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) && mCursor.x() < mGridSize.x() && mCursor.y() < mGridSize.y()) {
{
cursorEntry = getCellAt(mCursor); cursorEntry = getCellAt(mCursor);
if(cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry)
{ if (cursorEntry && cursorEntry->canFocus && cursorEntry != currentCursorEntry) {
onCursorMoved(origCursor, mCursor); onCursorMoved(origCursor, mCursor);
return true; return true;
} }
mCursor -= searchAxis; mCursor -= searchAxis;
} }
mCursor = curDirPos; mCursor = curDirPos;
} }
//failed to find another focusable element in this direction // Failed to find another focusable element in this direction.
mCursor = origCursor; mCursor = origCursor;
return false; return false;
} }
@ -324,14 +313,14 @@ bool ComponentGrid::moveCursor(Vector2i dir)
void ComponentGrid::onFocusLost() void ComponentGrid::onFocusLost()
{ {
const GridEntry* cursorEntry = getCellAt(mCursor); const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry) if (cursorEntry)
cursorEntry->component->onFocusLost(); cursorEntry->component->onFocusLost();
} }
void ComponentGrid::onFocusGained() void ComponentGrid::onFocusGained()
{ {
const GridEntry* cursorEntry = getCellAt(mCursor); const GridEntry* cursorEntry = getCellAt(mCursor);
if(cursorEntry) if (cursorEntry)
cursorEntry->component->onFocusGained(); cursorEntry->component->onFocusGained();
} }
@ -343,11 +332,12 @@ bool ComponentGrid::cursorValid()
void ComponentGrid::update(int deltaTime) void ComponentGrid::update(int deltaTime)
{ {
// update ALL THE THINGS // Update ALL THE THINGS.
const GridEntry* cursorEntry = getCellAt(mCursor); const GridEntry* cursorEntry = getCellAt(mCursor);
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++)
{ {
if(it->updateType == UPDATE_ALWAYS || (it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it))) if (it->updateType == UPDATE_ALWAYS ||
(it->updateType == UPDATE_WHEN_SELECTED && cursorEntry == &(*it)))
it->component->update(deltaTime); it->component->update(deltaTime);
} }
} }
@ -358,9 +348,8 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
renderChildren(trans); renderChildren(trans);
// draw cell separators // Draw cell separators.
if(mLines.size()) if (mLines.size()) {
{
Renderer::setMatrix(trans); Renderer::setMatrix(trans);
Renderer::bindTexture(0); Renderer::bindTexture(0);
Renderer::drawLines(&mLines[0], mLines.size()); Renderer::drawLines(&mLines[0], mLines.size());
@ -370,18 +359,18 @@ void ComponentGrid::render(const Transform4x4f& parentTrans)
void ComponentGrid::textInput(const char* text) void ComponentGrid::textInput(const char* text)
{ {
const GridEntry* selectedEntry = getCellAt(mCursor); const GridEntry* selectedEntry = getCellAt(mCursor);
if(selectedEntry != NULL && selectedEntry->canFocus) if (selectedEntry != NULL && selectedEntry->canFocus)
selectedEntry->component->textInput(text); selectedEntry->component->textInput(text);
} }
void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to) void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
{ {
const GridEntry* cell = getCellAt(from); const GridEntry* cell = getCellAt(from);
if(cell) if (cell)
cell->component->onFocusLost(); cell->component->onFocusLost();
cell = getCellAt(to); cell = getCellAt(to);
if(cell) if (cell)
cell->component->onFocusGained(); cell->component->onFocusGained();
updateHelpPrompts(); updateHelpPrompts();
@ -389,10 +378,8 @@ void ComponentGrid::onCursorMoved(Vector2i from, Vector2i to)
void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp) void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
{ {
for(auto it = mCells.cbegin(); it != mCells.cend(); it++) for (auto it = mCells.cbegin(); it != mCells.cend(); it++) {
{ if (it->component == comp) {
if(it->component == comp)
{
Vector2i oldCursor = mCursor; Vector2i oldCursor = mCursor;
mCursor = it->pos; mCursor = it->pos;
onCursorMoved(oldCursor, mCursor); onCursorMoved(oldCursor, mCursor);
@ -400,7 +387,7 @@ void ComponentGrid::setCursorTo(const std::shared_ptr<GuiComponent>& comp)
} }
} }
// component not found!! // Component not found!!
assert(false); assert(false);
} }
@ -408,32 +395,30 @@ std::vector<HelpPrompt> ComponentGrid::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts; std::vector<HelpPrompt> prompts;
const GridEntry* e = getCellAt(mCursor); const GridEntry* e = getCellAt(mCursor);
if(e) if (e)
prompts = e->component->getHelpPrompts(); prompts = e->component->getHelpPrompts();
bool canScrollVert = mGridSize.y() > 1; bool canScrollVert = mGridSize.y() > 1;
bool canScrollHoriz = mGridSize.x() > 1; bool canScrollHoriz = mGridSize.x() > 1;
for(auto it = prompts.cbegin(); it != prompts.cend(); it++) for (auto it = prompts.cbegin(); it != prompts.cend(); it++) {
{ if (it->first == "up/down/left/right") {
if(it->first == "up/down/left/right")
{
canScrollHoriz = false; canScrollHoriz = false;
canScrollVert = false; canScrollVert = false;
break; break;
}else if(it->first == "up/down") }
{ else if (it->first == "up/down") {
canScrollVert = false; canScrollVert = false;
}else if(it->first == "left/right") }
{ else if (it->first == "left/right") {
canScrollHoriz = false; canScrollHoriz = false;
} }
} }
if(canScrollHoriz && canScrollVert) if (canScrollHoriz && canScrollVert)
prompts.push_back(HelpPrompt("up/down/left/right", "choose")); prompts.push_back(HelpPrompt("up/down/left/right", "choose"));
else if(canScrollHoriz) else if (canScrollHoriz)
prompts.push_back(HelpPrompt("left/right", "choose")); prompts.push_back(HelpPrompt("left/right", "choose"));
else if(canScrollVert) else if (canScrollVert)
prompts.push_back(HelpPrompt("up/down", "choose")); prompts.push_back(HelpPrompt("up/down", "choose"));
return prompts; return prompts;

View file

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

View file

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

View file

@ -1,16 +1,32 @@
//
// DateTimeComponent.cpp
//
// Date and time component.
//
#include "components/DateTimeComponent.h" #include "components/DateTimeComponent.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "Log.h" #include "Log.h"
#include "Settings.h" #include "Settings.h"
DateTimeComponent::DateTimeComponent(Window* window) : TextComponent(window), mDisplayRelative(false) DateTimeComponent::DateTimeComponent(Window* window)
: TextComponent(window), mDisplayRelative(false)
{ {
setFormat("%m/%d/%Y"); setFormat("%m/%d/%Y");
} }
DateTimeComponent::DateTimeComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color, Alignment align, DateTimeComponent::DateTimeComponent(
Vector3f pos, Vector2f size, unsigned int bgcolor) : TextComponent(window, text, font, color, align, pos, size, bgcolor), mDisplayRelative(false) 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"); setFormat("%m/%d/%Y");
} }
@ -41,14 +57,13 @@ void DateTimeComponent::setDisplayRelative(bool displayRelative)
void DateTimeComponent::onTextChanged() void DateTimeComponent::onTextChanged()
{ {
mText = getDisplayString(); mText = getDisplayString();
TextComponent::onTextChanged(); TextComponent::onTextChanged();
} }
std::string DateTimeComponent::getDisplayString() const std::string DateTimeComponent::getDisplayString() const
{ {
if (mDisplayRelative) { if (mDisplayRelative) {
//relative time // Relative time.
if(mTime.getTime() == 0) if(mTime.getTime() == 0)
return "never"; 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); GuiComponent::applyTheme(theme, view, element, properties);
@ -106,8 +122,7 @@ void DateTimeComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, cons
setRenderBackground(true); setRenderBackground(true);
} }
if(properties & ALIGNMENT && elem->has("alignment")) if(properties & ALIGNMENT && elem->has("alignment")) {
{
std::string str = elem->get<std::string>("alignment"); std::string str = elem->get<std::string>("alignment");
if(str == "left") if(str == "left")
setHorizontalAlignment(ALIGN_LEFT); setHorizontalAlignment(ALIGN_LEFT);

View file

@ -1,3 +1,9 @@
//
// DateTimeComponent.h
//
// Date and time component.
//
#pragma once #pragma once
#ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H #ifndef ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
#define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H #define ES_CORE_COMPONENTS_DATE_TIME_COMPONENT_H
@ -12,8 +18,15 @@ class DateTimeComponent : public TextComponent
{ {
public: public:
DateTimeComponent(Window* window); DateTimeComponent(Window* window);
DateTimeComponent(Window* window, const std::string& text, const std::shared_ptr<Font>& font, unsigned int color = 0x000000FF, Alignment align = ALIGN_LEFT, DateTimeComponent(
Vector3f pos = Vector3f::Zero(), Vector2f size = Vector2f::Zero(), unsigned int bgcolor = 0x00000000); Window* window,
const std::string& text,
const std::shared_ptr<Font>& font,
unsigned int color = 0x000000FF,
Alignment align = ALIGN_LEFT,
Vector3f pos = Vector3f::Zero(),
Vector2f size = Vector2f::Zero(),
unsigned int bgcolor = 0x00000000);
void render(const Transform4x4f& parentTrans) override; void render(const Transform4x4f& parentTrans) override;
@ -23,7 +36,8 @@ public:
void setFormat(const std::string& format); void setFormat(const std::string& format);
void setDisplayRelative(bool displayRelative); void setDisplayRelative(bool displayRelative);
virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties) override; virtual void applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view,
const std::string& element, unsigned int properties) override;
protected: protected:
void onTextChanged() override; void onTextChanged() override;

View file

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

View file

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

View file

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

View file

@ -1,3 +1,9 @@
//
// MenuComponent.h
//
// Basic component for building a menu.
//
#pragma once #pragma once
#ifndef ES_CORE_COMPONENTS_MENU_COMPONENT_H #ifndef ES_CORE_COMPONENTS_MENU_COMPONENT_H
#define ES_CORE_COMPONENTS_MENU_COMPONENT_H #define ES_CORE_COMPONENTS_MENU_COMPONENT_H
@ -11,7 +17,8 @@
class ButtonComponent; class ButtonComponent;
class ImageComponent; 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); std::shared_ptr<ImageComponent> makeArrow(Window* window);
#define TITLE_VERT_PADDING (Renderer::getScreenHeight()*0.0637f) #define TITLE_VERT_PADDING (Renderer::getScreenHeight()*0.0637f)
@ -19,25 +26,30 @@ std::shared_ptr<ImageComponent> makeArrow(Window* window);
class MenuComponent : public GuiComponent class MenuComponent : public GuiComponent
{ {
public: 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(); virtual ~MenuComponent(); // just calls save();
void save(); void save();
void onSizeChanged() override; 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; 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); row.addElement(comp, false, invert_when_selected);
addRow(row, setCursorHere); addRow(row, setCursorHere);
} }
inline void addSaveFunc(const std::function<void()>& func) { mSaveFuncs.push_back(func); }; 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); 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 "SwitchComponent.h"
#include "resources/Font.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.setImage(":/off.svg");
mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight()); mImage.setResize(0, Font::get(FONT_SIZE_MEDIUM)->getLetterHeight());
@ -16,8 +27,7 @@ void SwitchComponent::onSizeChanged()
bool SwitchComponent::input(InputConfig* config, Input input) bool SwitchComponent::input(InputConfig* config, Input input)
{ {
if(config->isMappedTo("a", input) && input.value) if(config->isMappedTo("a", input) && input.value) {
{
mState = !mState; mState = !mState;
onStateChanged(); onStateChanged();
return true; return true;
@ -29,9 +39,7 @@ bool SwitchComponent::input(InputConfig* config, Input input)
void SwitchComponent::render(const Transform4x4f& parentTrans) void SwitchComponent::render(const Transform4x4f& parentTrans)
{ {
Transform4x4f trans = parentTrans * getTransform(); Transform4x4f trans = parentTrans * getTransform();
mImage.render(trans); mImage.render(trans);
renderChildren(trans); renderChildren(trans);
} }
@ -54,12 +62,9 @@ std::string SwitchComponent::getValue() const
void SwitchComponent::setValue(const std::string& statestring) void SwitchComponent::setValue(const std::string& statestring)
{ {
if (statestring == "true") if (statestring == "true")
{
mState = true; mState = true;
}else else
{
mState = false; mState = false;
}
onStateChanged(); onStateChanged();
} }

View file

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

View file

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

View file

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

View file

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