Added a virtual keyboard.

This commit is contained in:
Leon Styhre 2021-09-17 22:23:41 +02:00
parent bbaf2739d4
commit c4e6d3cac1
16 changed files with 1182 additions and 308 deletions

View file

@ -14,6 +14,7 @@
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiSettings.h" #include "guis/GuiSettings.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
#include "views/ViewController.h" #include "views/ViewController.h"
@ -208,10 +209,21 @@ GuiCollectionSystemsOptions::GuiCollectionSystemsOptions(Window* window, std::st
window->removeGui(topGui); window->removeGui(topGui);
createCustomCollection(name); createCustomCollection(name);
}; };
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
row.makeAcceptInputHandler([this, createCollectionCall] { row.makeAcceptInputHandler([this, createCollectionCall] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name", "", mWindow->pushGui(new GuiTextEditKeyboardPopup(
createCollectionCall, false, "SAVE")); mWindow, getHelpStyle(), "New Collection Name", "", createCollectionCall, false,
"CREATE", "CREATE COLLECTION?"));
}); });
}
else {
row.makeAcceptInputHandler([this, createCollectionCall] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "New Collection Name",
"", createCollectionCall, false, "CREATE",
"CREATE COLLECTION?"));
});
}
addRow(row); addRow(row);
// Delete custom collection. // Delete custom collection.

View file

@ -12,6 +12,7 @@
#include "SystemData.h" #include "SystemData.h"
#include "components/OptionListComponent.h" #include "components/OptionListComponent.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
#include "views/ViewController.h" #include "views/ViewController.h"
@ -118,11 +119,20 @@ void GuiGamelistFilter::addFiltersToMenu()
mFilterIndex->setTextFilter(Utils::String::toUpper(newVal)); mFilterIndex->setTextFilter(Utils::String::toUpper(newVal));
}; };
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
row.makeAcceptInputHandler([this, updateVal] { row.makeAcceptInputHandler([this, updateVal] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)", mWindow->pushGui(new GuiTextEditKeyboardPopup(
mTextFilterField->getValue(), updateVal, false, "OK", mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)", mTextFilterField->getValue(),
"APPLY CHANGES?")); updateVal, false, "OK", "APPLY CHANGES?"));
}); });
}
else {
row.makeAcceptInputHandler([this, updateVal] {
mWindow->pushGui(new GuiTextEditPopup(
mWindow, getHelpStyle(), "TEXT FILTER (GAME NAME)", mTextFilterField->getValue(),
updateVal, false, "OK", "APPLY CHANGES?"));
});
}
mMenu.addRow(row); mMenu.addRow(row);

View file

@ -22,12 +22,13 @@
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "guis/GuiAlternativeEmulators.h" #include "guis/GuiAlternativeEmulators.h"
#include "guis/GuiCollectionSystemsOptions.h" #include "guis/GuiCollectionSystemsOptions.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiDetectDevice.h" #include "guis/GuiDetectDevice.h"
#include "guis/GuiMediaViewerOptions.h" #include "guis/GuiMediaViewerOptions.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiScraperMenu.h" #include "guis/GuiScraperMenu.h"
#include "guis/GuiScreensaverOptions.h" #include "guis/GuiScreensaverOptions.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "views/gamelist/IGameListView.h" #include "views/gamelist/IGameListView.h"
@ -509,6 +510,18 @@ void GuiMenu::openUIOptions()
} }
}); });
// Enable virtual (on-screen) keyboard.
auto virtual_keyboard = std::make_shared<SwitchComponent>(mWindow);
virtual_keyboard->setState(Settings::getInstance()->getBool("VirtualKeyboard"));
s->addWithLabel("ENABLE VIRTUAL KEYBOARD", virtual_keyboard);
s->addSaveFunc([virtual_keyboard, s] {
if (virtual_keyboard->getState() != Settings::getInstance()->getBool("VirtualKeyboard")) {
Settings::getInstance()->setBool("VirtualKeyboard", virtual_keyboard->getState());
s->setNeedsSaving();
s->setInvalidateCachedBackground();
}
});
// Enable the 'Y' button for tagging games as favorites. // Enable the 'Y' button for tagging games as favorites.
auto favorites_add_button = std::make_shared<SwitchComponent>(mWindow); auto favorites_add_button = std::make_shared<SwitchComponent>(mWindow);
favorites_add_button->setState(Settings::getInstance()->getBool("FavoritesAddButton")); favorites_add_button->setState(Settings::getInstance()->getBool("FavoritesAddButton"));
@ -809,10 +822,20 @@ void GuiMenu::openOtherOptions()
rowMediaDir.makeAcceptInputHandler([this, titleMediaDir, mediaDirectoryStaticText, rowMediaDir.makeAcceptInputHandler([this, titleMediaDir, mediaDirectoryStaticText,
defaultDirectoryText, initValueMediaDir, updateValMediaDir, defaultDirectoryText, initValueMediaDir, updateValMediaDir,
multiLineMediaDir] { multiLineMediaDir] {
mWindow->pushGui(new GuiComplexTextEditPopup( if (Settings::getInstance()->getBool("VirtualKeyboard")) {
mWindow, getHelpStyle(), titleMediaDir, mediaDirectoryStaticText, defaultDirectoryText, mWindow->pushGui(new GuiTextEditKeyboardPopup(
mWindow, getHelpStyle(), titleMediaDir,
Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir, Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir,
multiLineMediaDir, "SAVE", "SAVE CHANGES?")); multiLineMediaDir, "SAVE", "SAVE CHANGES?", mediaDirectoryStaticText,
defaultDirectoryText, "load default directory"));
}
else {
mWindow->pushGui(new GuiTextEditPopup(
mWindow, getHelpStyle(), titleMediaDir,
Settings::getInstance()->getString("MediaDirectory"), updateValMediaDir,
multiLineMediaDir, "SAVE", "SAVE CHANGES?", mediaDirectoryStaticText,
defaultDirectoryText, "load default directory"));
}
}); });
s->addRow(rowMediaDir); s->addRow(rowMediaDir);

View file

@ -23,9 +23,9 @@
#include "components/RatingComponent.h" #include "components/RatingComponent.h"
#include "components/SwitchComponent.h" #include "components/SwitchComponent.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiGameScraper.h" #include "guis/GuiGameScraper.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "resources/Font.h" #include "resources/Font.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
@ -364,11 +364,20 @@ GuiMetaDataEd::GuiMetaDataEd(Window* window,
} }
}; };
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
mWindow->pushGui(new GuiTextEditKeyboardPopup(
mWindow, getHelpStyle(), title, ed->getValue(), updateVal, multiLine,
"apply", "APPLY CHANGES?", "", ""));
});
}
else {
row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] { row.makeAcceptInputHandler([this, title, ed, updateVal, multiLine] {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title, mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), title,
ed->getValue(), updateVal, multiLine, ed->getValue(), updateVal, multiLine,
"APPLY", "APPLY CHANGES?")); "APPLY", "APPLY CHANGES?"));
}); });
}
break; break;
} }
} }

View file

@ -29,6 +29,7 @@
#include "components/ScrollableContainer.h" #include "components/ScrollableContainer.h"
#include "components/TextComponent.h" #include "components/TextComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "resources/Font.h" #include "resources/Font.h"
#include "utils/StringUtil.h" #include "utils/StringUtil.h"
@ -808,8 +809,16 @@ void GuiScraperSearch::openInputScreen(ScraperSearchParams& params)
searchString = params.nameOverride; searchString = params.nameOverride;
} }
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH", searchString, if (Settings::getInstance()->getBool("VirtualKeyboard")) {
searchForFunc, false, "SEARCH", "APPLY CHANGES?")); mWindow->pushGui(new GuiTextEditKeyboardPopup(mWindow, getHelpStyle(), "REFINE SEARCH",
searchString, searchForFunc, false, "SEARCH",
"SEARCH USING REFINED NAME?"));
}
else {
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), "REFINE SEARCH",
searchString, searchForFunc, false, "SEARCH",
"SEARCH USING REFINED NAME?"));
}
} }
bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result, bool GuiScraperSearch::saveMetadata(const ScraperSearchResult& result,

View file

@ -16,6 +16,7 @@
#include "SystemData.h" #include "SystemData.h"
#include "Window.h" #include "Window.h"
#include "components/HelpComponent.h" #include "components/HelpComponent.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "views/ViewController.h" #include "views/ViewController.h"
#include "views/gamelist/IGameListView.h" #include "views/gamelist/IGameListView.h"
@ -193,15 +194,30 @@ void GuiSettings::addEditableTextComponent(const std::string label,
} }
}; };
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] { row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
// Never display the value if it's a password, instead set it to blank. // Never display the value if it's a password, instead set it to blank.
if (isPassword) if (isPassword)
mWindow->pushGui( mWindow->pushGui(new GuiTextEditKeyboardPopup(
new GuiTextEditPopup(mWindow, getHelpStyle(), label, "", updateVal, false)); mWindow, getHelpStyle(), label, "", updateVal, false, "SAVE", "SAVE CHANGES?"));
else else
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, ed->getValue(), mWindow->pushGui(new GuiTextEditKeyboardPopup(mWindow, getHelpStyle(), label,
updateVal, false)); ed->getValue(), updateVal, false,
"SAVE", "SAVE CHANGES?"));
}); });
}
else {
row.makeAcceptInputHandler([this, label, ed, updateVal, isPassword] {
if (isPassword)
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label, "", updateVal,
false, "SAVE", "SAVE CHANGES?"));
else
mWindow->pushGui(new GuiTextEditPopup(mWindow, getHelpStyle(), label,
ed->getValue(), updateVal, false, "SAVE",
"SAVE CHANGES?"));
});
}
assert(ed); assert(ed);
addRow(row); addRow(row);

View file

@ -26,6 +26,8 @@
#include "animations/MoveCameraAnimation.h" #include "animations/MoveCameraAnimation.h"
#include "guis/GuiInfoPopup.h" #include "guis/GuiInfoPopup.h"
#include "guis/GuiMenu.h" #include "guis/GuiMenu.h"
#include "guis/GuiTextEditKeyboardPopup.h"
#include "guis/GuiTextEditPopup.h"
#include "views/SystemView.h" #include "views/SystemView.h"
#include "views/UIModeController.h" #include "views/UIModeController.h"
#include "views/gamelist/DetailedGameListView.h" #include "views/gamelist/DetailedGameListView.h"
@ -135,15 +137,15 @@ void ViewController::noGamesDialog()
#else #else
currentROMDirectory = FileData::getROMDirectory(); currentROMDirectory = FileData::getROMDirectory();
#endif #endif
if (Settings::getInstance()->getBool("VirtualKeyboard")) {
mWindow->pushGui(new GuiComplexTextEditPopup( mWindow->pushGui(new GuiTextEditKeyboardPopup(
mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH", mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH", currentROMDirectory,
"Currently configured path:", currentROMDirectory, currentROMDirectory,
[this](const std::string& newROMDirectory) { [this](const std::string& newROMDirectory) {
Settings::getInstance()->setString("ROMDirectory", newROMDirectory); Settings::getInstance()->setString("ROMDirectory", newROMDirectory);
Settings::getInstance()->saveFile(); Settings::getInstance()->saveFile();
#if defined(_WIN64) #if defined(_WIN64)
mRomDirectory = Utils::String::replace(FileData::getROMDirectory(), "/", "\\"); mRomDirectory =
Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
#else #else
mRomDirectory = FileData::getROMDirectory(); mRomDirectory = FileData::getROMDirectory();
#endif #endif
@ -151,10 +153,36 @@ void ViewController::noGamesDialog()
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(), mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"ROM DIRECTORY SETTING SAVED, RESTART\n" "ROM DIRECTORY SETTING SAVED, RESTART\n"
"THE APPLICATION TO RESCAN THE SYSTEMS", "THE APPLICATION TO RESCAN THE SYSTEMS",
"OK", nullptr, "", nullptr, "", nullptr, true)); "OK", nullptr, "", nullptr, "", nullptr,
true));
}, },
false, "SAVE", "SAVE CHANGES?", "LOAD CURRENT", "LOAD CURRENTLY CONFIGURED VALUE", false, "SAVE", "SAVE CHANGES?", "Currently configured path:",
"CLEAR", "CLEAR (LEAVE BLANK TO RESET TO DEFAULT DIRECTORY)", false)); currentROMDirectory, "LOAD CURRENTLY CONFIGURED PATH",
"CLEAR (LEAVE BLANK TO RESET TO DEFAULT PATH)"));
}
else {
mWindow->pushGui(new GuiTextEditPopup(
mWindow, HelpStyle(), "ENTER ROM DIRECTORY PATH", currentROMDirectory,
[this](const std::string& newROMDirectory) {
Settings::getInstance()->setString("ROMDirectory", newROMDirectory);
Settings::getInstance()->saveFile();
#if defined(_WIN64)
mRomDirectory =
Utils::String::replace(FileData::getROMDirectory(), "/", "\\");
#else
mRomDirectory = FileData::getROMDirectory();
#endif
mNoGamesMessageBox->changeText(mNoGamesErrorMessage + mRomDirectory);
mWindow->pushGui(new GuiMsgBox(mWindow, HelpStyle(),
"ROM DIRECTORY SETTING SAVED, RESTART\n"
"THE APPLICATION TO RESCAN THE SYSTEMS",
"OK", nullptr, "", nullptr, "", nullptr,
true));
},
false, "SAVE", "SAVE CHANGES?", "Currently configured path:",
currentROMDirectory, "LOAD CURRENTLY CONFIGURED PATH",
"CLEAR (LEAVE BLANK TO RESET TO DEFAULT PATH)"));
}
}, },
"CREATE DIRECTORIES", "CREATE DIRECTORIES",
[this] { [this] {

View file

@ -15,7 +15,6 @@
#include "FileData.h" #include "FileData.h"
#include "GuiComponent.h" #include "GuiComponent.h"
#include "guis/GuiComplexTextEditPopup.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
#include "renderers/Renderer.h" #include "renderers/Renderer.h"

View file

@ -60,10 +60,10 @@ set(CORE_HEADERS
${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.h ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.h
# GUIs # GUIs
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiComplexTextEditPopup.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditKeyboardPopup.h
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.h ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.h
# Renderers # Renderers
@ -130,10 +130,10 @@ set(CORE_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/components/VideoVlcComponent.cpp
# GUIs # GUIs
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiComplexTextEditPopup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiDetectDevice.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiInputConfig.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiMsgBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditKeyboardPopup.cpp
${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/guis/GuiTextEditPopup.cpp
# Renderer # Renderer

View file

@ -180,6 +180,7 @@ void Settings::setDefaults()
mBoolMap["FavoritesStar"] = {true, true}; mBoolMap["FavoritesStar"] = {true, true};
mBoolMap["SpecialCharsASCII"] = {false, false}; mBoolMap["SpecialCharsASCII"] = {false, false};
mBoolMap["ListScrollOverlay"] = {false, false}; mBoolMap["ListScrollOverlay"] = {false, false};
mBoolMap["VirtualKeyboard"] = {true, true};
mBoolMap["FavoritesAddButton"] = {true, true}; mBoolMap["FavoritesAddButton"] = {true, true};
mBoolMap["RandomAddButton"] = {false, false}; mBoolMap["RandomAddButton"] = {false, false};
mBoolMap["GamelistFilters"] = {true, true}; mBoolMap["GamelistFilters"] = {true, true};

View file

@ -1,155 +0,0 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiComplexTextEditPopup.cpp
//
// Text edit popup with a title, two text strings, a text input box and buttons
// to load the second text string and to clear the input field.
// Intended for updating settings for configuration files and similar.
//
#include "guis/GuiComplexTextEditPopup.h"
#include "Window.h"
#include "components/ButtonComponent.h"
#include "components/MenuComponent.h"
#include "components/TextEditComponent.h"
#include "guis/GuiMsgBox.h"
GuiComplexTextEditPopup::GuiComplexTextEditPopup(
Window* window,
const HelpStyle& helpstyle,
const std::string& title,
const std::string& infoString1,
const std::string& infoString2,
const std::string& initValue,
const std::function<void(const std::string&)>& okCallback,
bool multiLine,
const std::string& acceptBtnText,
const std::string& saveConfirmationText,
const std::string& loadBtnText,
const std::string& loadBtnHelpText,
const std::string& clearBtnText,
const std::string& clearBtnHelpText,
bool hideCancelButton)
: GuiComponent(window)
, mHelpStyle(helpstyle)
, mBackground(window, ":/graphics/frame.svg")
, mGrid(window, glm::ivec2{1, 5})
, mMultiLine(multiLine)
, mInitValue(initValue)
, mOkCallback(okCallback)
, mSaveConfirmationText(saveConfirmationText)
, mHideCancelButton(hideCancelButton)
{
addChild(&mBackground);
addChild(&mGrid);
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
mInfoString1 = std::make_shared<TextComponent>(mWindow, infoString1, Font::get(FONT_SIZE_SMALL),
0x555555FF, ALIGN_CENTER);
mInfoString2 = std::make_shared<TextComponent>(mWindow, infoString2, Font::get(FONT_SIZE_SMALL),
0x555555FF, ALIGN_CENTER);
mText = std::make_shared<TextEditComponent>(mWindow);
mText->setValue(initValue);
std::vector<std::shared_ptr<ButtonComponent>> buttons;
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, acceptBtnText, acceptBtnText,
[this, okCallback] {
okCallback(mText->getValue());
delete this;
}));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, loadBtnText, loadBtnHelpText,
[this, infoString2] {
mText->setValue(infoString2);
mText->setCursor(0);
mText->setCursor(infoString2.size());
}));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, clearBtnText, clearBtnHelpText,
[this] { mText->setValue(""); }));
if (!mHideCancelButton)
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
[this] { delete this; }));
mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true);
mGrid.setEntry(mInfoString1, glm::ivec2{0, 1}, false, true);
mGrid.setEntry(mInfoString2, glm::ivec2{0, 2}, false, false);
mGrid.setEntry(mText, glm::ivec2{0, 3}, true, false, glm::ivec2{1, 1},
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 4}, true, false);
mGrid.setRowHeightPerc(1, 0.15f, true);
float textHeight = mText->getFont()->getHeight();
if (multiLine)
textHeight *= 6.0f;
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float infoWidth = glm::clamp(0.70f * aspectValue, 0.60f, 0.85f) * Renderer::getScreenWidth();
float windowWidth = glm::clamp(0.75f * aspectValue, 0.65f, 0.90f) * Renderer::getScreenWidth();
mText->setSize(0, textHeight);
mInfoString2->setSize(infoWidth, mInfoString2->getFont()->getHeight());
setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y +
mButtonGrid->getSize().y * 1.85f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
mText->startEditing();
}
void GuiComplexTextEditPopup::onSizeChanged()
{
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mText->setSize(mSize.x - 40.0f, mText->getSize().y);
// Update grid.
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y);
mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y / mSize.y);
mGrid.setSize(mSize);
}
bool GuiComplexTextEditPopup::input(InputConfig* config, Input input)
{
if (GuiComponent::input(config, input))
return true;
if (!mHideCancelButton) {
// Pressing back when not text editing closes us.
if (config->isMappedTo("b", input) && input.value) {
if (mText->getValue() != mInitValue) {
// Changes were made, ask if the user wants to save them.
mWindow->pushGui(new GuiMsgBox(
mWindow, mHelpStyle, mSaveConfirmationText, "YES",
[this] {
this->mOkCallback(mText->getValue());
delete this;
return true;
},
"NO",
[this] {
delete this;
return false;
}));
}
else {
delete this;
}
}
}
return false;
}
std::vector<HelpPrompt> GuiComplexTextEditPopup::getHelpPrompts()
{
std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts();
if (!mHideCancelButton)
prompts.push_back(HelpPrompt("b", "back"));
return prompts;
}

View file

@ -1,64 +0,0 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiComplexTextEditPopup.h
//
// Text edit popup with a title, two text strings, a text input box and buttons
// to load the second text string and to clear the input field.
// Intended for updating settings for configuration files and similar.
//
#ifndef ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H
#define ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H
#include "GuiComponent.h"
#include "components/ComponentGrid.h"
#include "components/NinePatchComponent.h"
class TextComponent;
class TextEditComponent;
class GuiComplexTextEditPopup : public GuiComponent
{
public:
GuiComplexTextEditPopup(Window* window,
const HelpStyle& helpstyle,
const std::string& title,
const std::string& infoString1,
const std::string& infoString2,
const std::string& initValue,
const std::function<void(const std::string&)>& okCallback,
bool multiLine,
const std::string& acceptBtnText = "OK",
const std::string& saveConfirmationText = "SAVE CHANGES?",
const std::string& loadBtnText = "LOAD",
const std::string& loadBtnHelpText = "load default",
const std::string& clearBtnText = "CLEAR",
const std::string& clearBtnHelpText = "clear",
bool hideCancelButton = false);
bool input(InputConfig* config, Input input) override;
void onSizeChanged() override;
std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override { return mHelpStyle; }
private:
NinePatchComponent mBackground;
ComponentGrid mGrid;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<TextComponent> mInfoString1;
std::shared_ptr<TextComponent> mInfoString2;
std::shared_ptr<TextEditComponent> mText;
std::shared_ptr<ComponentGrid> mButtonGrid;
HelpStyle mHelpStyle;
bool mMultiLine;
bool mHideCancelButton;
std::string mInitValue;
std::function<void(const std::string&)> mOkCallback;
std::string mSaveConfirmationText;
};
#endif // ES_CORE_GUIS_GUI_COMPLEX_TEXT_EDIT_POPUP_H

View file

@ -0,0 +1,697 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiTextEditKeyboardPopup.cpp
//
// Text edit popup with a virtual keyboard.
// Has a default mode and a complex mode, both with various options passed as arguments.
//
#define KEYBOARD_WIDTH Renderer::getScreenWidth() * 0.78f
#define KEYBOARD_HEIGHT Renderer::getScreenHeight() * 0.60f
#define KEYBOARD_PADDINGX (Renderer::getScreenWidth() * 0.02f)
#define KEYBOARD_PADDINGY (Renderer::getScreenWidth() * 0.01f)
#define BUTTON_GRID_HORIZ_PADDING (10.0f * Renderer::getScreenHeightModifier())
#define NAVIGATION_REPEAT_START_DELAY 400
#define NAVIGATION_REPEAT_SPEED 70 // Lower is faster.
#define DELETE_REPEAT_START_DELAY 600
#define DELETE_REPEAT_SPEED 90 // Lower is faster.
#if defined(_MSC_VER) // MSVC compiler.
#define DELETE_SYMBOL Utils::String::wideStringToString(L"\uf177")
#define OK_SYMBOL Utils::String::wideStringToString(L"\uf058")
#define SHIFT_SYMBOL Utils::String::wideStringToString(L"\uf176")
#define ALT_SYMBOL Utils::String::wideStringToString(L"\uf141")
#else
#define DELETE_SYMBOL "\uf177"
#define OK_SYMBOL "\uf058"
#define SHIFT_SYMBOL "\uf176"
#define ALT_SYMBOL "\uf141"
#endif
#include "guis/GuiTextEditKeyboardPopup.h"
#include "components/MenuComponent.h"
#include "guis/GuiMsgBox.h"
#include "utils/StringUtil.h"
// clang-format off
std::vector<std::vector<const char*>> kbBaseUS{
{"1", "2", "3", "4", "5", "6", "7", "8", "9", "0", "-", "=", "DEL"},
{"!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "+", "DEL"},
{"¡", "²", "³", "¤", "", "¼", "½", "¾", "", "", "¥", "×", "DEL"},
{"¹", "", "", "£", "", "", "", "", "", "", "", "÷", "DEL"},
{"q", "w", "e", "r", "t", "y", "u", "i", "o", "p", "[", "]", "OK"},
{"Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P", "{", "}", "OK"},
{"ä", "å", "é", "®", "þ", "ü", "ú", "í", "ó", "ö", "«", "»", "OK"},
{"Ä", "Å", "É", "", "Þ", "Ü", "Ú", "Í", "Ó", "Ö", "", "", "OK"},
{"a", "s", "d", "f", "g", "h", "j", "k", "l", ";", "'", "\\", "-rowspan-"},
{"A", "S", "D", "F", "G", "H", "J", "K", "L", ":", "\"", "|", "-rowspan-"},
{"á", "ß", "ð", "", "", "", "", "", "ø", "", "´", "¬", "-rowspan-"},
{"Á", "§", "Ð", "", "", "", "", "", "Ø", "°", "¨", "¦", "-rowspan-"},
{"`", "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "ALT", "-colspan-"},
{"~", "Z", "X", "C", "V", "B", "N", "M", "<", ">", "?", "ALT", "-colspan-"},
{"", "æ", "", "©", "", "", "ñ", "µ", "ç", "", "¿", "ALT", "-colspan-"},
{"", "Æ", "", "¢", "", "", "Ñ", "Μ", "Ç", "", "", "ALT", "-colspan-"}};
std::vector<std::vector<const char*>> kbLastRowNormal{
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"}};
std::vector<std::vector<const char*>> kbLastRowLoad{
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "LOAD", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "LOAD", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "LOAD", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"},
{"SHIFT", "-colspan-", "SPACE", "-colspan-", "-colspan-", "-colspan-", "-colspan-", "LOAD", "-colspan-", "CLEAR", "-colspan-", "CANCEL", "-colspan-"}};
// clang-format on
GuiTextEditKeyboardPopup::GuiTextEditKeyboardPopup(
Window* window,
const HelpStyle& helpstyle,
const std::string& title,
const std::string& initValue,
const std::function<void(const std::string&)>& okCallback,
bool multiLine,
const std::string& acceptBtnHelpText,
const std::string& saveConfirmationText,
const std::string& infoString,
const std::string& defaultValue,
const std::string& loadBtnHelpText,
const std::string& clearBtnHelpText,
const std::string& cancelBtnHelpText)
: GuiComponent{window}
, mHelpStyle{helpstyle}
, mInitValue{initValue}
, mOkCallback{okCallback}
, mMultiLine{multiLine}
, mAcceptBtnHelpText{acceptBtnHelpText}
, mSaveConfirmationText{saveConfirmationText}
, mLoadBtnHelpText{loadBtnHelpText}
, mClearBtnHelpText{clearBtnHelpText}
, mCancelBtnHelpText{cancelBtnHelpText}
, mBackground{window, ":/graphics/frame.svg"}
, mGrid{window, glm::ivec2{1, (infoString != "" && defaultValue != "" ? 8 : 6)}}
, mComplexMode{(infoString != "" && defaultValue != "")}
, mDeleteRepeat{false}
, mShift{false}
, mAlt{false}
, mAltShift{false}
, mDeleteRepeatTimer{0}
, mNavigationRepeatTimer{0}
, mNavigationRepeatDirX{0}
, mNavigationRepeatDirY{0}
{
addChild(&mBackground);
addChild(&mGrid);
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
Font::get(FONT_SIZE_LARGE), 0x555555FF, ALIGN_CENTER);
std::vector<std::vector<const char*>> kbLayout;
// At the moment there is only the US keyboard layout available.
kbLayout.insert(kbLayout.cend(), kbBaseUS.cbegin(), kbBaseUS.cend());
// In complex mode, the last row of the keyboard contains an additional "LOAD" button.
if (mComplexMode)
kbLayout.insert(kbLayout.cend(), kbLastRowLoad.cbegin(), kbLastRowLoad.cend());
else
kbLayout.insert(kbLayout.cend(), kbLastRowNormal.cbegin(), kbLastRowNormal.cend());
mHorizontalKeyCount = static_cast<int>(kbLayout[0].size());
mKeyboardGrid = std::make_shared<ComponentGrid>(
mWindow, glm::ivec2(mHorizontalKeyCount, static_cast<int>(kbLayout.size()) / 3));
mText = std::make_shared<TextEditComponent>(mWindow);
mText->setValue(initValue);
if (!multiLine)
mText->setCursor(initValue.size());
// Header.
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true);
int yPos = 1;
if (mComplexMode) {
mInfoString = std::make_shared<TextComponent>(
mWindow, infoString, Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mInfoString, glm::ivec2{0, yPos}, false, true);
mDefaultValue = std::make_shared<TextComponent>(
mWindow, defaultValue, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
mGrid.setEntry(mDefaultValue, glm::ivec2{0, yPos + 1}, false, true);
yPos += 2;
}
// Text edit field.
mGrid.setEntry(mText, glm::ivec2{0, yPos}, true, false, glm::ivec2{1, 1},
GridFlags::BORDER_TOP);
std::vector<std::vector<std::shared_ptr<ButtonComponent>>> buttonList;
// Create keyboard.
for (int i = 0; i < static_cast<int>(kbLayout.size()) / 4; i++) {
std::vector<std::shared_ptr<ButtonComponent>> buttons;
for (int j = 0; j < static_cast<int>(kbLayout[i].size()); j++) {
std::string lower = kbLayout[4 * i][j];
if (lower.empty() || lower == "-rowspan-" || lower == "-colspan-")
continue;
std::string upper = kbLayout[4 * i + 1][j];
std::string alted = kbLayout[4 * i + 2][j];
std::string altshifted = kbLayout[4 * i + 3][j];
std::shared_ptr<ButtonComponent> button = nullptr;
if (lower == "DEL") {
lower = DELETE_SYMBOL;
upper = DELETE_SYMBOL;
alted = DELETE_SYMBOL;
altshifted = DELETE_SYMBOL;
}
else if (lower == "OK") {
lower = OK_SYMBOL;
upper = OK_SYMBOL;
alted = OK_SYMBOL;
altshifted = OK_SYMBOL;
}
else if (lower == "SPACE") {
lower = " ";
upper = " ";
alted = " ";
altshifted = " ";
}
else if (lower != "SHIFT" && lower.length() > 1) {
lower = (lower.c_str());
upper = (upper.c_str());
alted = (alted.c_str());
altshifted = (altshifted.c_str());
}
if (lower == "SHIFT") {
mShiftButton = std::make_shared<ButtonComponent>(
mWindow, (SHIFT_SYMBOL), ("SHIFT"), [this] { shiftKeys(); }, false, true);
button = mShiftButton;
}
else if (lower == "ALT") {
mAltButton = std::make_shared<ButtonComponent>(
mWindow, (ALT_SYMBOL), ("ALT"), [this] { altKeys(); }, false, true);
button = mAltButton;
}
else {
button = makeButton(lower, upper, alted, altshifted);
}
button->setPadding(
glm::vec4(BUTTON_GRID_HORIZ_PADDING / 4.0f, BUTTON_GRID_HORIZ_PADDING / 4.0f,
BUTTON_GRID_HORIZ_PADDING / 4.0f, BUTTON_GRID_HORIZ_PADDING / 4.0f));
buttons.push_back(button);
int colSpan = 1;
for (int cs = j + 1; cs < static_cast<int>(kbLayout[i].size()); cs++) {
if (std::string(kbLayout[4 * i][cs]) == "-colspan-")
colSpan++;
else
break;
}
int rowSpan = 1;
for (int cs = (4 * i) + 4; cs < static_cast<int>(kbLayout.size()); cs += 4) {
if (std::string(kbLayout[cs][j]) == "-rowspan-")
rowSpan++;
else
break;
}
mKeyboardGrid->setEntry(button, glm::ivec2{j, i}, true, true,
glm::ivec2{colSpan, rowSpan});
buttonList.push_back(buttons);
}
}
mGrid.setEntry(mKeyboardGrid, glm::ivec2{0, yPos + 1}, true, true, glm::ivec2{2, 4});
float textHeight = mText->getFont()->getHeight();
// If the multiLine option has been set, then include three lines of text on screen.
if (multiLine) {
textHeight *= 3.0f;
textHeight += 2.0f * Renderer::getScreenHeightModifier();
}
mText->setSize(0.0f, textHeight);
// If attempting to navigate beyond the edge of the keyboard grid, then wrap around.
mGrid.setPastBoundaryCallback([this, kbLayout](InputConfig* config, Input input) -> bool {
if (config->isMappedLike("left", input)) {
if (mGrid.getSelectedComponent() == mKeyboardGrid) {
mKeyboardGrid->moveCursorTo(mHorizontalKeyCount - 1, -1, true);
return true;
}
}
else if (config->isMappedLike("right", input)) {
if (mGrid.getSelectedComponent() == mKeyboardGrid) {
mKeyboardGrid->moveCursorTo(0, -1);
return true;
}
}
return false;
});
// Adapt width to the geometry of the display. The 1.778 aspect ratio is the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float width = glm::clamp(0.78f * aspectValue, 0.35f, 0.90f) * Renderer::getScreenWidth();
// The combination of multiLine and complex mode is not supported as there is currently
// no need for that.
if (mMultiLine) {
setSize(width, KEYBOARD_HEIGHT + textHeight - mText->getFont()->getHeight());
setPosition((static_cast<float>(Renderer::getScreenWidth()) - mSize.x) / 2.0f,
(static_cast<float>(Renderer::getScreenHeight()) - mSize.y) / 2.0f);
}
else {
if (mComplexMode)
setSize(width, KEYBOARD_HEIGHT + mDefaultValue->getSize().y * 3.0f);
else
setSize(width, KEYBOARD_HEIGHT);
setPosition((static_cast<float>(Renderer::getScreenWidth()) - mSize.x) / 2.0f,
(static_cast<float>(Renderer::getScreenHeight()) - mSize.y) / 2.0f);
}
}
void GuiTextEditKeyboardPopup::onSizeChanged()
{
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mText->setSize(mSize.x - KEYBOARD_PADDINGX - KEYBOARD_PADDINGX, mText->getSize().y);
// Update grid.
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y);
if (mInfoString && mDefaultValue) {
mGrid.setRowHeightPerc(1, (mInfoString->getSize().y * 0.6f) / mSize.y);
mGrid.setRowHeightPerc(2, (mDefaultValue->getSize().y * 1.6f) / mSize.y);
mGrid.setRowHeightPerc(1, (mText->getSize().y * 1.0f) / mSize.y);
}
else if (mMultiLine) {
mGrid.setRowHeightPerc(1, (mText->getSize().y * 1.15f) / mSize.y);
}
mGrid.setSize(mSize);
auto pos = mKeyboardGrid->getPosition();
auto sz = mKeyboardGrid->getSize();
// Add a small margin between buttons.
mKeyboardGrid->setSize(mSize.x - KEYBOARD_PADDINGX - KEYBOARD_PADDINGX,
sz.y - KEYBOARD_PADDINGY + 70.0f * Renderer::getScreenHeightModifier());
mKeyboardGrid->setPosition(KEYBOARD_PADDINGX, pos.y);
}
bool GuiTextEditKeyboardPopup::input(InputConfig* config, Input input)
{
// Enter/return key or numpad enter key accepts the changes.
if (config->getDeviceId() == DEVICE_KEYBOARD && mText->isEditing() && !mMultiLine &&
input.value && (input.id == SDLK_RETURN || input.id == SDLK_KP_ENTER)) {
this->mOkCallback(mText->getValue());
delete this;
return true;
}
// Dito for the A button if using a controller.
else if (config->getDeviceId() != DEVICE_KEYBOARD && mText->isEditing() &&
config->isMappedTo("a", input) && input.value) {
this->mOkCallback(mText->getValue());
delete this;
return true;
}
// If the keyboard has been configured with backspace as the back button (which is the default
// configuration) then ignore this key if we're currently editing or otherwise it would be
// impossible to erase characters using this key.
bool keyboardBackspace = (config->getDeviceId() == DEVICE_KEYBOARD && mText->isEditing() &&
input.id == SDLK_BACKSPACE);
// Pressing back (or the escape key if using keyboard input) closes us.
if ((config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_ESCAPE) ||
(!keyboardBackspace && config->isMappedTo("b", input)) && input.value) {
if (mText->getValue() != mInitValue) {
// Changes were made, ask if the user wants to save them.
mWindow->pushGui(new GuiMsgBox(
mWindow, mHelpStyle, mSaveConfirmationText, "YES",
[this] {
this->mOkCallback(mText->getValue());
delete this;
return true;
},
"NO",
[this] {
delete this;
return false;
}));
}
else {
delete this;
}
}
if (mText->isEditing() && config->isMappedLike("down", input) && input.value) {
mText->stopEditing();
mGrid.setCursorTo(mGrid.getSelectedComponent());
}
// Left trigger button outside text editing field toggles Shift key.
if (!mText->isEditing() && config->isMappedLike("lefttrigger", input) && input.value)
shiftKeys();
// Right trigger button outside text editing field toggles Alt key.
if (!mText->isEditing() && config->isMappedLike("righttrigger", input) && input.value)
altKeys();
// Left shoulder button deletes a character (backspace).
if (config->isMappedTo("leftshoulder", input)) {
if (input.value) {
mDeleteRepeat = true;
mDeleteRepeatTimer = -(DELETE_REPEAT_START_DELAY - DELETE_REPEAT_SPEED);
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput("\b");
if (!editing)
mText->stopEditing();
}
else {
mDeleteRepeat = false;
}
return true;
}
// Right shoulder button inserts a blank space.
if (config->isMappedTo("rightshoulder", input) && input.value) {
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput(" ");
if (!editing)
mText->stopEditing();
return true;
}
// Actual navigation of the keyboard grid is done in ComponentGrid, this code only handles
// key repeat while holding the left/right/up/down buttons.
if (!mText->isEditing() && config->isMappedLike("left", input)) {
if (input.value) {
mNavigationRepeatDirX = -1;
mNavigationRepeatTimer = -(NAVIGATION_REPEAT_START_DELAY - NAVIGATION_REPEAT_SPEED);
}
else {
mNavigationRepeatDirX = 0;
}
}
if (!mText->isEditing() && config->isMappedLike("right", input)) {
if (input.value) {
mNavigationRepeatDirX = 1;
mNavigationRepeatTimer = -(NAVIGATION_REPEAT_START_DELAY - NAVIGATION_REPEAT_SPEED);
}
else {
mNavigationRepeatDirX = 0;
}
}
if (!mText->isEditing() && config->isMappedLike("up", input)) {
if (input.value) {
mNavigationRepeatDirY = -1;
mNavigationRepeatTimer = -(NAVIGATION_REPEAT_START_DELAY - NAVIGATION_REPEAT_SPEED);
}
else {
mNavigationRepeatDirY = 0;
}
}
if (!mText->isEditing() && config->isMappedLike("down", input)) {
if (input.value) {
mNavigationRepeatDirY = 1;
mNavigationRepeatTimer = -(NAVIGATION_REPEAT_START_DELAY - NAVIGATION_REPEAT_SPEED);
}
else {
mNavigationRepeatDirY = 0;
}
}
if (GuiComponent::input(config, input))
return true;
return false;
}
void GuiTextEditKeyboardPopup::update(int deltaTime)
{
updateNavigationRepeat(deltaTime);
updateDeleteRepeat(deltaTime);
GuiComponent::update(deltaTime);
}
std::vector<HelpPrompt> GuiTextEditKeyboardPopup::getHelpPrompts()
{
std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts();
if (!mText->isEditing()) {
prompts.push_back(HelpPrompt("lt", "shift"));
prompts.push_back(HelpPrompt("rt", "alt"));
}
else {
prompts.push_back(HelpPrompt("a", mAcceptBtnHelpText));
}
prompts.push_back(HelpPrompt("l", "backspace"));
prompts.push_back(HelpPrompt("r", "space"));
prompts.push_back(HelpPrompt("b", "back"));
if (prompts.size() > 0 && prompts.front().second == OK_SYMBOL)
prompts.front().second = mAcceptBtnHelpText;
if (prompts.size() > 0 && prompts.front().second == " ")
prompts.front().second = "SPACE";
if (prompts.size() > 0 && prompts.front().second == "CLEAR")
prompts.front().second = mClearBtnHelpText;
if (prompts.size() > 0 && prompts.front().second == "LOAD")
prompts.front().second = mLoadBtnHelpText;
if (prompts.size() > 0 && prompts.front().second == "CANCEL")
prompts.front().second = mCancelBtnHelpText;
// If a prompt has no value set, then remove it.
if (prompts.size() > 0 && prompts.front().second == "")
prompts.erase(prompts.begin(), prompts.begin() + 1);
return prompts;
}
void GuiTextEditKeyboardPopup::updateDeleteRepeat(int deltaTime)
{
if (!mDeleteRepeat)
return;
mDeleteRepeatTimer += deltaTime;
while (mDeleteRepeatTimer >= DELETE_REPEAT_SPEED) {
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput("\b");
if (!editing)
mText->stopEditing();
mDeleteRepeatTimer -= DELETE_REPEAT_SPEED;
}
}
void GuiTextEditKeyboardPopup::updateNavigationRepeat(int deltaTime)
{
if (mNavigationRepeatDirX == 0 && mNavigationRepeatDirY == 0)
return;
mNavigationRepeatTimer += deltaTime;
while (mNavigationRepeatTimer >= NAVIGATION_REPEAT_SPEED) {
if (mNavigationRepeatDirX != 0) {
mKeyboardGrid.get()->moveCursor({mNavigationRepeatDirX, 0});
// If replacing the line above with this code, the keyboard will wrap around the
// edges also when key repeat is active.
// if (!mKeyboardGrid.get()->moveCursor({mNavigationRepeatDirX, 0})) {
// if (mNavigationRepeatDirX < 0)
// mKeyboardGrid->moveCursorTo(mHorizontalKeyCount - 1, -1);
// else
// mKeyboardGrid->moveCursorTo(0, -1);
// }
}
if (mNavigationRepeatDirY != 0)
mKeyboardGrid.get()->moveCursor({0, mNavigationRepeatDirY});
mNavigationRepeatTimer -= NAVIGATION_REPEAT_SPEED;
}
}
void GuiTextEditKeyboardPopup::shiftKeys()
{
mShift = !mShift;
if (mShift) {
mShiftButton->setFlatColorFocused(0xFF2222FF);
mShiftButton->setFlatColorUnfocused(0xFF2222FF);
}
else {
mShiftButton->setFlatColorFocused(0x878787FF);
mShiftButton->setFlatColorUnfocused(0x60606025);
}
if (mAlt && mShift) {
altShiftKeys();
return;
}
// This only happens when Alt was deselected while both Shift and Alt were active.
if (mAlt) {
altKeys();
altKeys();
}
else {
for (auto& kb : mKeyboardButtons) {
const std::string& text = mShift ? kb.shiftedKey : kb.key;
auto sz = kb.button->getSize();
kb.button->setText(text, text, false);
kb.button->setSize(sz);
}
}
}
void GuiTextEditKeyboardPopup::altKeys()
{
mAlt = !mAlt;
if (mAlt) {
mAltButton->setFlatColorFocused(0xFF2222FF);
mAltButton->setFlatColorUnfocused(0xFF2222FF);
}
else {
mAltButton->setFlatColorFocused(0x878787FF);
mAltButton->setFlatColorUnfocused(0x60606025);
}
if (mShift && mAlt) {
altShiftKeys();
return;
}
// This only happens when Shift was deselected while both Shift and Alt were active.
if (mShift) {
shiftKeys();
shiftKeys();
}
else {
for (auto& kb : mKeyboardButtons) {
const std::string& text = mAlt ? kb.altedKey : kb.key;
auto sz = kb.button->getSize();
kb.button->setText(text, text, false);
kb.button->setSize(sz);
}
}
}
void GuiTextEditKeyboardPopup::altShiftKeys()
{
for (auto& kb : mKeyboardButtons) {
const std::string& text = kb.altshiftedKey;
auto sz = kb.button->getSize();
kb.button->setText(text, text, false);
kb.button->setSize(sz);
}
}
std::shared_ptr<ButtonComponent> GuiTextEditKeyboardPopup::makeButton(
const std::string& key,
const std::string& shiftedKey,
const std::string& altedKey,
const std::string& altshiftedKey)
{
std::shared_ptr<ButtonComponent> button = std::make_shared<ButtonComponent>(
mWindow, key, key,
[this, key, shiftedKey, altedKey, altshiftedKey] {
if (key == (OK_SYMBOL) || key.find("OK") != std::string::npos) {
mOkCallback(mText->getValue());
delete this;
return;
}
else if (key == (DELETE_SYMBOL) || key == "DEL") {
mText->startEditing();
mText->textInput("\b");
mText->stopEditing();
return;
}
else if (key == "SPACE" || key == " ") {
mText->startEditing();
mText->textInput(" ");
mText->stopEditing();
return;
}
else if (key == "LOAD") {
mText->setValue(mDefaultValue->getValue());
mText->setCursor(mDefaultValue->getValue().size());
return;
}
else if (key == "CLEAR") {
mText->setValue("");
return;
}
else if (key == "CANCEL") {
delete this;
return;
}
if (mAlt && altedKey.empty())
return;
mText->startEditing();
if (mShift && mAlt)
mText->textInput(altshiftedKey.c_str());
else if (mAlt)
mText->textInput(altedKey.c_str());
else if (mShift)
mText->textInput(shiftedKey.c_str());
else
mText->textInput(key.c_str());
mText->stopEditing();
},
false, true);
KeyboardButton kb(button, key, shiftedKey, altedKey, altshiftedKey);
mKeyboardButtons.push_back(kb);
return button;
}

View file

@ -0,0 +1,112 @@
// SPDX-License-Identifier: MIT
//
// EmulationStation Desktop Edition
// GuiTextEditKeyboardPopup.h
//
// Text edit popup with a virtual keyboard.
// Has a default mode and a complex mode, both with various options passed as arguments.
//
#ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_KEYBOARD_POPUP_H
#define ES_CORE_GUIS_GUI_TEXT_EDIT_KEYBOARD_POPUP_H
#include "GuiComponent.h"
#include "components/ButtonComponent.h"
#include "components/ComponentGrid.h"
#include "components/TextEditComponent.h"
class GuiTextEditKeyboardPopup : public GuiComponent
{
public:
GuiTextEditKeyboardPopup(Window* window,
const HelpStyle& helpstyle,
const std::string& title,
const std::string& initValue,
const std::function<void(const std::string&)>& okCallback,
bool multiLine,
const std::string& acceptBtnHelpText = "OK",
const std::string& saveConfirmationText = "SAVE CHANGES?",
const std::string& infoString = "",
const std::string& defaultValue = "",
const std::string& loadBtnHelpText = "LOAD DEFAULT",
const std::string& clearBtnHelpText = "CLEAR",
const std::string& cancelBtnHelpText = "DISCARD CHANGES");
void onSizeChanged();
bool input(InputConfig* config, Input input);
void update(int deltaTime) override;
std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override { return mHelpStyle; }
private:
class KeyboardButton
{
public:
std::shared_ptr<ButtonComponent> button;
const std::string key;
const std::string shiftedKey;
const std::string altedKey;
const std::string altshiftedKey;
KeyboardButton(const std::shared_ptr<ButtonComponent> b,
const std::string& k,
const std::string& sk,
const std::string& ak,
const std::string& ask)
: button{b}
, key{k}
, shiftedKey{sk}
, altedKey{ak}
, altshiftedKey{ask} {};
};
void updateDeleteRepeat(int deltaTime);
void updateNavigationRepeat(int deltaTime);
void shiftKeys();
void altKeys();
void altShiftKeys();
std::shared_ptr<ButtonComponent> makeButton(const std::string& key,
const std::string& shiftedKey,
const std::string& altedKey,
const std::string& altshiftedKey);
std::vector<KeyboardButton> mKeyboardButtons;
std::shared_ptr<ButtonComponent> mShiftButton;
std::shared_ptr<ButtonComponent> mAltButton;
NinePatchComponent mBackground;
ComponentGrid mGrid;
HelpStyle mHelpStyle;
std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<TextComponent> mInfoString;
std::shared_ptr<TextComponent> mDefaultValue;
std::shared_ptr<TextEditComponent> mText;
std::shared_ptr<ComponentGrid> mKeyboardGrid;
std::string mInitValue;
std::string mAcceptBtnHelpText;
std::string mSaveConfirmationText;
std::string mLoadBtnHelpText;
std::string mClearBtnHelpText;
std::string mCancelBtnHelpText;
std::function<void(const std::string&)> mOkCallback;
bool mMultiLine;
bool mComplexMode;
bool mDeleteRepeat;
bool mShift;
bool mAlt;
bool mAltShift;
int mHorizontalKeyCount;
int mDeleteRepeatTimer;
int mNavigationRepeatTimer;
int mNavigationRepeatDirX;
int mNavigationRepeatDirY;
};
#endif // ES_CORE_GUIS_GUI_TEXT_EDIT_KEYBOARD_POPUP_H

View file

@ -3,15 +3,16 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// GuiTextEditPopup.cpp // GuiTextEditPopup.cpp
// //
// Simple text edit popup with a title, a text input box and OK and Cancel buttons. // Text edit popup.
// Has a default mode and a complex mode, both with various options passed as arguments.
// //
#define DELETE_REPEAT_START_DELAY 600
#define DELETE_REPEAT_SPEED 90 // Lower is faster.
#include "guis/GuiTextEditPopup.h" #include "guis/GuiTextEditPopup.h"
#include "Window.h"
#include "components/ButtonComponent.h"
#include "components/MenuComponent.h" #include "components/MenuComponent.h"
#include "components/TextEditComponent.h"
#include "guis/GuiMsgBox.h" #include "guis/GuiMsgBox.h"
GuiTextEditPopup::GuiTextEditPopup(Window* window, GuiTextEditPopup::GuiTextEditPopup(Window* window,
@ -21,15 +22,27 @@ GuiTextEditPopup::GuiTextEditPopup(Window* window,
const std::function<void(const std::string&)>& okCallback, const std::function<void(const std::string&)>& okCallback,
bool multiLine, bool multiLine,
const std::string& acceptBtnText, const std::string& acceptBtnText,
const std::string& saveConfirmationText) const std::string& saveConfirmationText,
: GuiComponent(window) const std::string& infoString,
, mHelpStyle(helpstyle) const std::string& defaultValue,
, mBackground(window, ":/graphics/frame.svg") const std::string& loadBtnHelpText,
, mGrid(window, glm::ivec2{1, 3}) const std::string& clearBtnHelpText,
, mMultiLine(multiLine) const std::string& cancelBtnHelpText)
, mInitValue(initValue) : GuiComponent{window}
, mOkCallback(okCallback) , mHelpStyle{helpstyle}
, mSaveConfirmationText(saveConfirmationText) , mInitValue{initValue}
, mOkCallback{okCallback}
, mMultiLine{multiLine}
, mAcceptBtnText{acceptBtnText}
, mSaveConfirmationText{saveConfirmationText}
, mLoadBtnHelpText{loadBtnHelpText}
, mClearBtnHelpText{clearBtnHelpText}
, mCancelBtnHelpText{cancelBtnHelpText}
, mBackground{window, ":/graphics/frame.svg"}
, mGrid{window, glm::ivec2{1, (infoString != "" && defaultValue != "" ? 5 : 3)}}
, mComplexMode{(infoString != "" && defaultValue != "")}
, mDeleteRepeat{false}
, mDeleteRepeatTimer{0}
{ {
addChild(&mBackground); addChild(&mBackground);
addChild(&mGrid); addChild(&mGrid);
@ -37,6 +50,13 @@ GuiTextEditPopup::GuiTextEditPopup(Window* window,
mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title), mTitle = std::make_shared<TextComponent>(mWindow, Utils::String::toUpper(title),
Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER); Font::get(FONT_SIZE_MEDIUM), 0x555555FF, ALIGN_CENTER);
if (mComplexMode) {
mInfoString = std::make_shared<TextComponent>(
mWindow, infoString, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
mDefaultValue = std::make_shared<TextComponent>(
mWindow, defaultValue, Font::get(FONT_SIZE_SMALL), 0x555555FF, ALIGN_CENTER);
}
mText = std::make_shared<TextEditComponent>(mWindow); mText = std::make_shared<TextEditComponent>(mWindow);
mText->setValue(initValue); mText->setValue(initValue);
@ -46,55 +66,116 @@ GuiTextEditPopup::GuiTextEditPopup(Window* window,
okCallback(mText->getValue()); okCallback(mText->getValue());
delete this; delete this;
})); }));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CLEAR", "clear", if (mComplexMode) {
buttons.push_back(std::make_shared<ButtonComponent>(
mWindow, "load", loadBtnHelpText, [this, defaultValue] {
mText->setValue(defaultValue);
mText->setCursor(0);
mText->setCursor(defaultValue.size());
}));
}
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "clear", clearBtnHelpText,
[this] { mText->setValue(""); })); [this] { mText->setValue(""); }));
buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes", buttons.push_back(std::make_shared<ButtonComponent>(mWindow, "CANCEL", "discard changes",
[this] { delete this; })); [this] { delete this; }));
mButtonGrid = makeButtonGrid(mWindow, buttons); mButtonGrid = makeButtonGrid(mWindow, buttons);
mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true); mGrid.setEntry(mTitle, glm::ivec2{0, 0}, false, true);
mGrid.setEntry(mText, glm::ivec2{0, 1}, true, false, glm::ivec2{1, 1},
int yPos = 1;
if (mComplexMode) {
mGrid.setEntry(mInfoString, glm::ivec2{0, yPos}, false, true);
mGrid.setEntry(mDefaultValue, glm::ivec2{0, yPos + 1}, false, false);
yPos += 2;
}
mGrid.setEntry(mText, glm::ivec2{0, yPos}, true, false, glm::ivec2{1, 1},
GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM); GridFlags::BORDER_TOP | GridFlags::BORDER_BOTTOM);
mGrid.setEntry(mButtonGrid, glm::ivec2{0, 2}, true, false); mGrid.setEntry(mButtonGrid, glm::ivec2{0, yPos + 1}, true, false);
float textHeight = mText->getFont()->getHeight(); float textHeight = mText->getFont()->getHeight();
if (multiLine) if (multiLine)
textHeight *= 6.0f; textHeight *= 6.0f;
mText->setSize(0, textHeight); mText->setSize(0, textHeight);
// Adjust the width relative to the aspect ratio of the screen to make the GUI look coherent // Adapt width to the geometry of the display. The 1.778 aspect ratio is the 16:9 reference.
// regardless of screen type. The 1.778 aspect ratio value is the 16:9 reference.
float aspectValue = 1.778f / Renderer::getScreenAspectRatio(); float aspectValue = 1.778f / Renderer::getScreenAspectRatio();
float width = glm::clamp(0.50f * aspectValue, 0.40f, 0.70f) * Renderer::getScreenWidth();
if (mComplexMode) {
float infoWidth =
glm::clamp(0.70f * aspectValue, 0.34f, 0.85f) * Renderer::getScreenWidth();
float windowWidth =
glm::clamp(0.75f * aspectValue, 0.40f, 0.90f) * Renderer::getScreenWidth();
mDefaultValue->setSize(infoWidth, mDefaultValue->getFont()->getHeight());
setSize(windowWidth, mTitle->getFont()->getHeight() + textHeight +
mButtonGrid->getSize().y + mButtonGrid->getSize().y * 1.85f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f);
}
else {
float width = glm::clamp(0.54f * aspectValue, 0.20f, 0.70f) * Renderer::getScreenWidth();
setSize(width, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y + setSize(width, mTitle->getFont()->getHeight() + textHeight + mButtonGrid->getSize().y +
mButtonGrid->getSize().y / 2.0f); mButtonGrid->getSize().y / 2.0f);
setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f, setPosition((Renderer::getScreenWidth() - mSize.x) / 2.0f,
(Renderer::getScreenHeight() - mSize.y) / 2.0f); (Renderer::getScreenHeight() - mSize.y) / 2.0f);
}
if (!multiLine)
mText->setCursor(initValue.size());
mText->startEditing(); mText->startEditing();
} }
void GuiTextEditPopup::onSizeChanged() void GuiTextEditPopup::onSizeChanged()
{ {
mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f}); mBackground.fitTo(mSize, glm::vec3{}, glm::vec2{-32.0f, -32.0f});
mText->setSize(mSize.x - 40.0f * Renderer::getScreenHeightModifier(), mText->getSize().y);
mText->setSize(mSize.x - 40.0f, mText->getSize().y);
// Update grid. // Update grid.
mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y); mGrid.setRowHeightPerc(0, mTitle->getFont()->getHeight() / mSize.y);
if (mComplexMode)
mGrid.setRowHeightPerc(1, 0.15f);
mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y / mSize.y); mGrid.setRowHeightPerc(2, mButtonGrid->getSize().y / mSize.y);
mGrid.setSize(mSize); mGrid.setSize(mSize);
} }
bool GuiTextEditPopup::input(InputConfig* config, Input input) bool GuiTextEditPopup::input(InputConfig* config, Input input)
{ {
if (GuiComponent::input(config, input)) // Enter key (main key or via numpad) accepts the changes.
if (config->getDeviceId() == DEVICE_KEYBOARD && mText->isEditing() && !mMultiLine &&
input.value && (input.id == SDLK_RETURN || input.id == SDLK_KP_ENTER)) {
this->mOkCallback(mText->getValue());
delete this;
return true; return true;
}
// Dito for the A button if using a controller.
else if (config->getDeviceId() != DEVICE_KEYBOARD && mText->isEditing() &&
config->isMappedTo("a", input) && input.value) {
this->mOkCallback(mText->getValue());
delete this;
return true;
}
// Pressing back when not text editing closes us. // If the keyboard has been configured with backspace as the back button (which is the default
if (config->isMappedTo("b", input) && input.value) { // configuration) then ignore this key if we're currently editing or otherwise it would be
// impossible to erase characters using this key.
bool keyboardBackspace = (config->getDeviceId() == DEVICE_KEYBOARD && mText->isEditing() &&
input.id == SDLK_BACKSPACE);
// Pressing back (or the escape key if using keyboard input) closes us.
if ((config->getDeviceId() == DEVICE_KEYBOARD && input.value && input.id == SDLK_ESCAPE) ||
(!keyboardBackspace && config->isMappedTo("b", input)) && input.value) {
if (mText->getValue() != mInitValue) { if (mText->getValue() != mInitValue) {
// Changes were made, ask if the user wants to save them. // Changes were made, ask if the user wants to save them.
mWindow->pushGui(new GuiMsgBox( mWindow->pushGui(new GuiMsgBox(
@ -114,12 +195,89 @@ bool GuiTextEditPopup::input(InputConfig* config, Input input)
delete this; delete this;
} }
} }
if (mText->isEditing() && config->isMappedLike("down", input) && input.value) {
mText->stopEditing();
mGrid.setCursorTo(mGrid.getSelectedComponent());
}
// Left shoulder button deletes a character (backspace).
if (config->isMappedTo("leftshoulder", input)) {
if (input.value) {
mDeleteRepeat = true;
mDeleteRepeatTimer = -(DELETE_REPEAT_START_DELAY - DELETE_REPEAT_SPEED);
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput("\b");
if (!editing)
mText->stopEditing();
}
else {
mDeleteRepeat = false;
}
return true;
}
// Right shoulder button inserts a blank space.
if (config->isMappedTo("rightshoulder", input) && input.value) {
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput(" ");
if (!editing)
mText->stopEditing();
return true;
}
if (GuiComponent::input(config, input))
return true;
return false; return false;
} }
void GuiTextEditPopup::update(int deltaTime)
{
updateDeleteRepeat(deltaTime);
GuiComponent::update(deltaTime);
}
std::vector<HelpPrompt> GuiTextEditPopup::getHelpPrompts() std::vector<HelpPrompt> GuiTextEditPopup::getHelpPrompts()
{ {
std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts(); std::vector<HelpPrompt> prompts = mGrid.getHelpPrompts();
if (mText->isEditing())
prompts.push_back(HelpPrompt("a", mAcceptBtnText));
prompts.push_back(HelpPrompt("l", "backspace"));
prompts.push_back(HelpPrompt("r", "space"));
prompts.push_back(HelpPrompt("b", "back")); prompts.push_back(HelpPrompt("b", "back"));
return prompts; return prompts;
} }
void GuiTextEditPopup::updateDeleteRepeat(int deltaTime)
{
if (!mDeleteRepeat)
return;
mDeleteRepeatTimer += deltaTime;
while (mDeleteRepeatTimer >= DELETE_REPEAT_SPEED) {
bool editing = mText->isEditing();
if (!editing)
mText->startEditing();
mText->textInput("\b");
if (!editing)
mText->stopEditing();
mDeleteRepeatTimer -= DELETE_REPEAT_SPEED;
}
}

View file

@ -3,18 +3,17 @@
// EmulationStation Desktop Edition // EmulationStation Desktop Edition
// GuiTextEditPopup.h // GuiTextEditPopup.h
// //
// Simple text edit popup with a title, a text input box and OK and Cancel buttons. // Text edit popup.
// Has a default mode and a complex mode, both with various options passed as arguments.
// //
#ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H #ifndef ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
#define ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H #define ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H
#include "GuiComponent.h" #include "GuiComponent.h"
#include "components/ButtonComponent.h"
#include "components/ComponentGrid.h" #include "components/ComponentGrid.h"
#include "components/NinePatchComponent.h" #include "components/TextEditComponent.h"
class TextComponent;
class TextEditComponent;
class GuiTextEditPopup : public GuiComponent class GuiTextEditPopup : public GuiComponent
{ {
@ -26,27 +25,47 @@ public:
const std::function<void(const std::string&)>& okCallback, const std::function<void(const std::string&)>& okCallback,
bool multiLine, bool multiLine,
const std::string& acceptBtnText = "OK", const std::string& acceptBtnText = "OK",
const std::string& saveConfirmationText = "SAVE CHANGES?"); const std::string& saveConfirmationText = "SAVE CHANGES?",
const std::string& infoString = "",
const std::string& defaultValue = "",
const std::string& loadBtnHelpText = "LOAD DEFAULT",
const std::string& clearBtnHelpText = "CLEAR",
const std::string& cancelBtnHelpText = "DISCARD CHANGES");
bool input(InputConfig* config, Input input) override;
void onSizeChanged() override; void onSizeChanged() override;
bool input(InputConfig* config, Input input) override;
void update(int deltaTime) override;
std::vector<HelpPrompt> getHelpPrompts() override; std::vector<HelpPrompt> getHelpPrompts() override;
HelpStyle getHelpStyle() override { return mHelpStyle; } HelpStyle getHelpStyle() override { return mHelpStyle; }
private: private:
void updateDeleteRepeat(int deltaTime);
NinePatchComponent mBackground; NinePatchComponent mBackground;
ComponentGrid mGrid; ComponentGrid mGrid;
HelpStyle mHelpStyle;
std::shared_ptr<TextComponent> mTitle; std::shared_ptr<TextComponent> mTitle;
std::shared_ptr<TextComponent> mInfoString;
std::shared_ptr<TextComponent> mDefaultValue;
std::shared_ptr<TextEditComponent> mText; std::shared_ptr<TextEditComponent> mText;
std::shared_ptr<ComponentGrid> mButtonGrid; std::shared_ptr<ComponentGrid> mButtonGrid;
HelpStyle mHelpStyle;
bool mMultiLine;
std::string mInitValue; std::string mInitValue;
std::function<void(const std::string&)> mOkCallback; std::string mAcceptBtnText;
std::string mSaveConfirmationText; std::string mSaveConfirmationText;
std::string mLoadBtnHelpText;
std::string mClearBtnHelpText;
std::string mCancelBtnHelpText;
std::function<void(const std::string&)> mOkCallback;
bool mMultiLine;
bool mComplexMode;
bool mDeleteRepeat;
int mDeleteRepeatTimer;
}; };
#endif // ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H #endif // ES_CORE_GUIS_GUI_TEXT_EDIT_POPUP_H