2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-28 16:39:18 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-28 16:39:18 +00:00
|
|
|
// HelpComponent.cpp
|
|
|
|
//
|
|
|
|
// Help information in icon and text pairs.
|
|
|
|
//
|
|
|
|
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/HelpComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
#include "Log.h"
|
|
|
|
#include "Settings.h"
|
2021-08-22 14:43:15 +00:00
|
|
|
#include "Window.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "components/ComponentGrid.h"
|
2014-06-20 01:30:09 +00:00
|
|
|
#include "components/ImageComponent.h"
|
|
|
|
#include "components/TextComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "resources/TextureResource.h"
|
2018-01-27 17:04:28 +00:00
|
|
|
#include "utils/StringUtil.h"
|
2014-01-25 23:34:29 +00:00
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
static std::map<std::string, std::string> sIconPathMap{};
|
2014-01-25 23:34:29 +00:00
|
|
|
|
2021-07-07 18:31:46 +00:00
|
|
|
HelpComponent::HelpComponent(Window* window)
|
|
|
|
: GuiComponent(window)
|
2014-01-25 23:34:29 +00:00
|
|
|
{
|
2021-05-23 17:12:31 +00:00
|
|
|
assignIcons();
|
|
|
|
}
|
|
|
|
|
|
|
|
void HelpComponent::assignIcons()
|
|
|
|
{
|
|
|
|
std::string controllerType = Settings::getInstance()->getString("InputControllerType");
|
|
|
|
|
2021-08-30 12:29:43 +00:00
|
|
|
std::map<std::string, std::string> sIconPathMapOld(sIconPathMap);
|
2021-05-23 17:12:31 +00:00
|
|
|
sIconPathMap.clear();
|
|
|
|
|
|
|
|
// These graphics files are common between all controller types.
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["up/down"] = mStyle.mCustomButtons.dpad_updown.empty() ?
|
|
|
|
":/help/dpad_updown.svg" :
|
|
|
|
mStyle.mCustomButtons.dpad_updown;
|
|
|
|
sIconPathMap["left/right"] = mStyle.mCustomButtons.dpad_leftright.empty() ?
|
|
|
|
":/help/dpad_leftright.svg" :
|
|
|
|
mStyle.mCustomButtons.dpad_leftright;
|
|
|
|
sIconPathMap["up/down/left/right"] = mStyle.mCustomButtons.dpad_all.empty() ?
|
|
|
|
":/help/dpad_all.svg" :
|
|
|
|
mStyle.mCustomButtons.dpad_all;
|
|
|
|
sIconPathMap["thumbstickclick"] = mStyle.mCustomButtons.thumbstick_click.empty() ?
|
|
|
|
":/help/thumbstick_click.svg" :
|
|
|
|
mStyle.mCustomButtons.thumbstick_click;
|
|
|
|
sIconPathMap["l"] = mStyle.mCustomButtons.button_l.empty() ? ":/help/button_l.svg" :
|
|
|
|
mStyle.mCustomButtons.button_l;
|
|
|
|
sIconPathMap["r"] = mStyle.mCustomButtons.button_r.empty() ? ":/help/button_r.svg" :
|
|
|
|
mStyle.mCustomButtons.button_r;
|
|
|
|
sIconPathMap["lr"] = mStyle.mCustomButtons.button_lr.empty() ? ":/help/button_lr.svg" :
|
|
|
|
mStyle.mCustomButtons.button_lr;
|
2021-09-17 19:14:43 +00:00
|
|
|
sIconPathMap["lt"] = mStyle.mCustomButtons.button_lt.empty() ? ":/help/button_lt.svg" :
|
|
|
|
mStyle.mCustomButtons.button_lt;
|
|
|
|
sIconPathMap["rt"] = mStyle.mCustomButtons.button_rt.empty() ? ":/help/button_rt.svg" :
|
|
|
|
mStyle.mCustomButtons.button_rt;
|
2021-05-23 17:12:31 +00:00
|
|
|
|
|
|
|
// These graphics files are custom per controller type.
|
|
|
|
if (controllerType == "snes") {
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_SNES.empty() ?
|
|
|
|
":/help/button_a_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_a_SNES;
|
|
|
|
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_SNES.empty() ?
|
|
|
|
":/help/button_b_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_b_SNES;
|
|
|
|
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_SNES.empty() ?
|
|
|
|
":/help/button_x_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_x_SNES;
|
|
|
|
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_SNES.empty() ?
|
|
|
|
":/help/button_y_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_y_SNES;
|
|
|
|
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_SNES.empty() ?
|
|
|
|
":/help/button_start_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_start_SNES;
|
|
|
|
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_SNES.empty() ?
|
|
|
|
":/help/button_back_SNES.svg" :
|
|
|
|
mStyle.mCustomButtons.button_back_SNES;
|
2021-05-23 17:12:31 +00:00
|
|
|
}
|
|
|
|
else if (controllerType == "ps4") {
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ?
|
|
|
|
":/help/button_a_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_a_PS;
|
|
|
|
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ?
|
|
|
|
":/help/button_b_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_b_PS;
|
|
|
|
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ?
|
|
|
|
":/help/button_x_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_x_PS;
|
|
|
|
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ?
|
|
|
|
":/help/button_y_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_y_PS;
|
|
|
|
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS4.empty() ?
|
|
|
|
":/help/button_start_PS4.svg" :
|
|
|
|
mStyle.mCustomButtons.button_start_PS4;
|
|
|
|
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS4.empty() ?
|
|
|
|
":/help/button_back_PS4.svg" :
|
|
|
|
mStyle.mCustomButtons.button_back_PS4;
|
2021-05-23 17:12:31 +00:00
|
|
|
}
|
|
|
|
else if (controllerType == "ps5") {
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_PS.empty() ?
|
|
|
|
":/help/button_a_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_a_PS;
|
|
|
|
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_PS.empty() ?
|
|
|
|
":/help/button_b_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_b_PS;
|
|
|
|
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_PS.empty() ?
|
|
|
|
":/help/button_x_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_x_PS;
|
|
|
|
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_PS.empty() ?
|
|
|
|
":/help/button_y_PS.svg" :
|
|
|
|
mStyle.mCustomButtons.button_y_PS;
|
|
|
|
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_PS5.empty() ?
|
|
|
|
":/help/button_start_PS5.svg" :
|
|
|
|
mStyle.mCustomButtons.button_start_PS5;
|
|
|
|
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_PS5.empty() ?
|
|
|
|
":/help/button_back_PS5.svg" :
|
|
|
|
mStyle.mCustomButtons.button_back_PS5;
|
2021-05-23 17:12:31 +00:00
|
|
|
}
|
|
|
|
else if (controllerType == "xbox360") {
|
2021-08-30 12:29:43 +00:00
|
|
|
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ?
|
|
|
|
":/help/button_a_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_a_XBOX;
|
|
|
|
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ?
|
|
|
|
":/help/button_b_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_b_XBOX;
|
|
|
|
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ?
|
|
|
|
":/help/button_x_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_x_XBOX;
|
|
|
|
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ?
|
|
|
|
":/help/button_y_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_y_XBOX;
|
|
|
|
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX360.empty() ?
|
|
|
|
":/help/button_start_XBOX360.svg" :
|
|
|
|
mStyle.mCustomButtons.button_start_XBOX360;
|
|
|
|
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX360.empty() ?
|
|
|
|
":/help/button_back_XBOX360.svg" :
|
|
|
|
mStyle.mCustomButtons.button_back_XBOX360;
|
2021-05-23 17:12:31 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Xbox One and later.
|
2021-08-22 15:51:19 +00:00
|
|
|
sIconPathMap["a"] = mStyle.mCustomButtons.button_a_XBOX.empty() ?
|
|
|
|
":/help/button_a_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_a_XBOX;
|
|
|
|
sIconPathMap["b"] = mStyle.mCustomButtons.button_b_XBOX.empty() ?
|
|
|
|
":/help/button_b_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_b_XBOX;
|
|
|
|
sIconPathMap["x"] = mStyle.mCustomButtons.button_x_XBOX.empty() ?
|
|
|
|
":/help/button_x_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_x_XBOX;
|
|
|
|
sIconPathMap["y"] = mStyle.mCustomButtons.button_y_XBOX.empty() ?
|
|
|
|
":/help/button_y_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_y_XBOX;
|
|
|
|
sIconPathMap["start"] = mStyle.mCustomButtons.button_start_XBOX.empty() ?
|
|
|
|
":/help/button_start_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_start_XBOX;
|
|
|
|
sIconPathMap["back"] = mStyle.mCustomButtons.button_back_XBOX.empty() ?
|
|
|
|
":/help/button_back_XBOX.svg" :
|
|
|
|
mStyle.mCustomButtons.button_back_XBOX;
|
2021-05-23 17:12:31 +00:00
|
|
|
}
|
2021-08-30 12:29:43 +00:00
|
|
|
|
|
|
|
// Invalidate cache for icons that have changed.
|
|
|
|
auto it = sIconPathMap.begin();
|
|
|
|
while (it != sIconPathMap.end()) {
|
|
|
|
if (sIconPathMapOld.find(it->first) != sIconPathMapOld.end()) {
|
|
|
|
if (sIconPathMapOld[it->first] != sIconPathMap[it->first]) {
|
|
|
|
if (mIconCache.find(it->first) != mIconCache.end()) {
|
|
|
|
mIconCache.erase(mIconCache.find(it->first));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
it++;
|
|
|
|
}
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HelpComponent::clearPrompts()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mPrompts.clear();
|
|
|
|
updateGrid();
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-03-24 01:33:27 +00:00
|
|
|
void HelpComponent::setPrompts(const std::vector<HelpPrompt>& prompts)
|
2014-01-25 23:34:29 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mPrompts = prompts;
|
|
|
|
updateGrid();
|
2014-03-24 01:33:27 +00:00
|
|
|
}
|
2014-01-25 23:34:29 +00:00
|
|
|
|
2014-05-29 20:41:47 +00:00
|
|
|
void HelpComponent::setStyle(const HelpStyle& style)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mStyle = style;
|
|
|
|
updateGrid();
|
2021-08-22 15:51:19 +00:00
|
|
|
assignIcons();
|
2014-05-29 20:41:47 +00:00
|
|
|
}
|
|
|
|
|
2014-03-24 01:33:27 +00:00
|
|
|
void HelpComponent::updateGrid()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (!Settings::getInstance()->getBool("ShowHelpPrompts") || mPrompts.empty()) {
|
|
|
|
mGrid.reset();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<Font>& font = mStyle.font;
|
|
|
|
|
2020-11-17 22:06:54 +00:00
|
|
|
mGrid = std::make_shared<ComponentGrid>(mWindow,
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::ivec2{static_cast<int>(mPrompts.size()) * 4, 1});
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
// [icon] [spacer1] [text] [spacer2]
|
|
|
|
|
2021-03-27 09:26:13 +00:00
|
|
|
std::vector<std::shared_ptr<ImageComponent>> icons;
|
|
|
|
std::vector<std::shared_ptr<TextComponent>> labels;
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
float width = 0;
|
2020-12-28 10:29:32 +00:00
|
|
|
const float height = std::round(font->getLetterHeight() * 1.25f);
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-08-22 14:43:15 +00:00
|
|
|
// State variable indicating whether gui is dimmed.
|
|
|
|
bool isDimmed = mWindow->isBackgroundDimmed();
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
for (auto it = mPrompts.cbegin(); it != mPrompts.cend(); it++) {
|
|
|
|
auto icon = std::make_shared<ImageComponent>(mWindow);
|
|
|
|
icon->setImage(getIconTexture(it->first.c_str()));
|
2021-08-22 14:43:15 +00:00
|
|
|
icon->setColorShift(isDimmed ? mStyle.iconColorDimmed : mStyle.iconColor);
|
2020-06-28 16:39:18 +00:00
|
|
|
icon->setResize(0, height);
|
|
|
|
icons.push_back(icon);
|
|
|
|
|
2021-08-23 13:01:30 +00:00
|
|
|
// Apply text style and color from the theme to the label and add it to the label list.
|
2021-08-20 15:51:36 +00:00
|
|
|
std::string lblInput = it->second;
|
2021-08-23 13:01:30 +00:00
|
|
|
if (mStyle.textStyle == "lowercase")
|
2021-08-20 15:51:36 +00:00
|
|
|
lblInput = Utils::String::toLower(lblInput);
|
2021-08-23 13:01:30 +00:00
|
|
|
else if (mStyle.textStyle == "camelcase")
|
2021-08-20 15:51:36 +00:00
|
|
|
lblInput = Utils::String::toCamelCase(lblInput);
|
2021-08-23 13:01:30 +00:00
|
|
|
else
|
2021-08-20 15:51:36 +00:00
|
|
|
lblInput = Utils::String::toUpper(lblInput);
|
2021-08-22 14:43:15 +00:00
|
|
|
auto lbl = std::make_shared<TextComponent>(
|
|
|
|
mWindow, lblInput, font, isDimmed ? mStyle.textColorDimmed : mStyle.textColor);
|
2020-06-28 16:39:18 +00:00
|
|
|
labels.push_back(lbl);
|
|
|
|
|
2021-08-20 15:20:05 +00:00
|
|
|
width +=
|
|
|
|
icon->getSize().x + lbl->getSize().x +
|
|
|
|
((mStyle.iconTextSpacing + mStyle.entrySpacing) * Renderer::getScreenWidthModifier());
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
mGrid->setSize(width, height);
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < icons.size(); i++) {
|
2021-07-02 15:32:55 +00:00
|
|
|
const int col = i * 4;
|
2021-08-16 16:25:01 +00:00
|
|
|
mGrid->setColWidthPerc(col, icons.at(i)->getSize().x / width);
|
2021-08-20 15:20:05 +00:00
|
|
|
mGrid->setColWidthPerc(
|
|
|
|
col + 1, (mStyle.iconTextSpacing * Renderer::getScreenWidthModifier()) / width);
|
2021-08-16 16:25:01 +00:00
|
|
|
mGrid->setColWidthPerc(col + 2, labels.at(i)->getSize().x / width);
|
2020-06-28 16:39:18 +00:00
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
mGrid->setEntry(icons.at(i), glm::ivec2{col, 0}, false, false);
|
|
|
|
mGrid->setEntry(labels.at(i), glm::ivec2{col + 2, 0}, false, false);
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
|
2021-08-17 16:41:45 +00:00
|
|
|
mGrid->setPosition({mStyle.position.x, mStyle.position.y, 0.0f});
|
2020-06-28 16:39:18 +00:00
|
|
|
mGrid->setOrigin(mStyle.origin);
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<TextureResource> HelpComponent::getIconTexture(const char* name)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
auto it = mIconCache.find(name);
|
|
|
|
if (it != mIconCache.cend())
|
|
|
|
return it->second;
|
|
|
|
|
2021-05-23 17:12:31 +00:00
|
|
|
auto pathLookup = sIconPathMap.find(name);
|
|
|
|
if (pathLookup == sIconPathMap.cend()) {
|
2021-04-05 11:26:25 +00:00
|
|
|
LOG(LogError) << "Unknown help icon \"" << name << "\"";
|
2020-06-28 16:39:18 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
2021-08-30 12:29:43 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
if (!ResourceManager::getInstance()->fileExists(pathLookup->second)) {
|
2021-07-07 18:31:46 +00:00
|
|
|
LOG(LogError) << "Couldn't load help icon \"" << name << "\" as the file \""
|
|
|
|
<< pathLookup->second << "\" is missing";
|
2020-06-28 16:39:18 +00:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2021-04-05 11:26:25 +00:00
|
|
|
std::shared_ptr<TextureResource> tex =
|
2021-07-07 18:31:46 +00:00
|
|
|
TextureResource::get(pathLookup->second, false, false, false);
|
2020-06-28 16:39:18 +00:00
|
|
|
mIconCache[std::string(name)] = tex;
|
|
|
|
return tex;
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-15 01:58:16 +00:00
|
|
|
void HelpComponent::setOpacity(unsigned char opacity)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
GuiComponent::setOpacity(opacity);
|
2014-05-15 01:58:16 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
for (unsigned int i = 0; i < mGrid->getChildCount(); i++)
|
|
|
|
mGrid->getChild(i)->setOpacity(opacity);
|
2014-05-15 01:58:16 +00:00
|
|
|
}
|
|
|
|
|
2021-08-15 17:30:31 +00:00
|
|
|
void HelpComponent::render(const glm::mat4& parentTrans)
|
2014-01-25 23:34:29 +00:00
|
|
|
{
|
2021-08-17 16:41:45 +00:00
|
|
|
glm::mat4 trans{parentTrans * getTransform()};
|
2019-08-25 15:23:02 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
if (mGrid)
|
|
|
|
mGrid->render(trans);
|
2014-01-25 23:34:29 +00:00
|
|
|
}
|