123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372 |
- /*
- * CTextInput.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
- #include "StdInc.h"
- #include "CTextInput.h"
- #include "Images.h"
- #include "TextControls.h"
- #include "../gui/CGuiHandler.h"
- #include "../gui/Shortcut.h"
- #include "../render/Graphics.h"
- #include "../render/IFont.h"
- #include "../../lib/TextOperations.h"
- std::list<CFocusable *> CFocusable::focusables;
- CFocusable * CFocusable::inputWithFocus;
- CTextInput::CTextInput(const Rect & Pos)
- :originalAlignment(ETextAlignment::CENTERLEFT)
- {
- pos += Pos.topLeft();
- pos.h = Pos.h;
- pos.w = Pos.w;
- addUsedEvents(LCLICK | KEYBOARD | TEXTINPUT);
- }
- void CTextInput::createLabel(bool giveFocusToInput)
- {
- OBJ_CONSTRUCTION;
- label = std::make_shared<CLabel>();
- label->pos = pos;
- label->alignment = originalAlignment;
- #if !defined(VCMI_MOBILE)
- if(giveFocusToInput)
- giveFocus();
- #endif
- }
- CTextInput::CTextInput(const Rect & Pos, EFonts font, ETextAlignment alignment, bool giveFocusToInput)
- : CTextInput(Pos)
- {
- originalAlignment = alignment;
- setRedrawParent(true);
- createLabel(giveFocusToInput);
- setFont(font);
- setAlignment(alignment);
- }
- CTextInput::CTextInput(const Rect & Pos, const Point & bgOffset, const ImagePath & bgName)
- : CTextInput(Pos)
- {
- OBJ_CONSTRUCTION;
- if (!bgName.empty())
- background = std::make_shared<CPicture>(bgName, bgOffset.x, bgOffset.y);
- else
- setRedrawParent(true);
- createLabel(true);
- }
- CTextInput::CTextInput(const Rect & Pos, std::shared_ptr<IImage> srf)
- : CTextInput(Pos)
- {
- OBJ_CONSTRUCTION;
- background = std::make_shared<CPicture>(srf, Pos);
- pos.w = background->pos.w;
- pos.h = background->pos.h;
- background->pos = pos;
- createLabel(true);
- }
- void CTextInput::setFont(EFonts font)
- {
- label->font = font;
- }
- void CTextInput::setColor(const ColorRGBA & color)
- {
- label->color = color;
- }
- void CTextInput::setAlignment(ETextAlignment alignment)
- {
- originalAlignment = alignment;
- label->alignment = alignment;
- }
- const std::string & CTextInput::getText() const
- {
- return currentText;
- }
- void CTextInput::setCallback(const TextEditedCallback & cb)
- {
- assert(!onTextEdited);
- onTextEdited = cb;
- }
- void CTextInput::setFilterFilename()
- {
- assert(!onTextFiltering);
- onTextFiltering = std::bind(&CTextInput::filenameFilter, _1, _2);
- }
- void CTextInput::setFilterNumber(int minValue, int maxValue)
- {
- onTextFiltering = std::bind(&CTextInput::numberFilter, _1, _2, minValue, maxValue);
- }
- std::string CTextInput::getVisibleText() const
- {
- return hasFocus() ? currentText + composedText + "_" : currentText;
- }
- void CTextInput::clickPressed(const Point & cursorPosition)
- {
- // attempt to give focus unconditionally, even if we already have it
- // this forces on-screen keyboard to show up again, even if player have closed it before
- giveFocus();
- }
- void CTextInput::keyPressed(EShortcut key)
- {
- if(!hasFocus())
- return;
- if(key == EShortcut::GLOBAL_MOVE_FOCUS)
- {
- moveFocus();
- return;
- }
- bool redrawNeeded = false;
- switch(key)
- {
- case EShortcut::GLOBAL_BACKSPACE:
- if(!composedText.empty())
- {
- TextOperations::trimRightUnicode(composedText);
- redrawNeeded = true;
- }
- else if(!currentText.empty())
- {
- TextOperations::trimRightUnicode(currentText);
- redrawNeeded = true;
- }
- break;
- default:
- break;
- }
- if(redrawNeeded)
- {
- updateLabel();
- if(onTextEdited)
- onTextEdited(currentText);
- }
- }
- void CTextInput::setText(const std::string & nText)
- {
- currentText = nText;
- updateLabel();
- }
- void CTextInput::updateLabel()
- {
- std::string visibleText = getVisibleText();
- label->alignment = originalAlignment;
- while (graphics->fonts[label->font]->getStringWidth(visibleText) > pos.w)
- {
- label->alignment = ETextAlignment::CENTERRIGHT;
- visibleText = visibleText.substr(TextOperations::getUnicodeCharacterSize(visibleText[0]));
- }
- label->setText(visibleText);
- }
- void CTextInput::textInputed(const std::string & enteredText)
- {
- if(!hasFocus())
- return;
- std::string oldText = currentText;
- setText(getText() + enteredText);
- if(onTextFiltering)
- onTextFiltering(currentText, oldText);
- if(currentText != oldText)
- {
- updateLabel();
- if(onTextEdited)
- onTextEdited(currentText);
- }
- composedText.clear();
- }
- void CTextInput::textEdited(const std::string & enteredText)
- {
- if(!hasFocus())
- return;
- composedText = enteredText;
- updateLabel();
- }
- void CTextInput::filenameFilter(std::string & text, const std::string &oldText)
- {
- static const std::string forbiddenChars = "<>:\"/\\|?*\r\n"; //if we are entering a filename, some special characters won't be allowed
- size_t pos;
- while((pos = text.find_first_of(forbiddenChars)) != std::string::npos)
- text.erase(pos, 1);
- }
- void CTextInput::numberFilter(std::string & text, const std::string & oldText, int minValue, int maxValue)
- {
- assert(minValue < maxValue);
- if(text.empty())
- text = "0";
- size_t pos = 0;
- if(text[0] == '-') //allow '-' sign as first symbol only
- pos++;
- while(pos < text.size())
- {
- if(text[pos] < '0' || text[pos] > '9')
- {
- text = oldText;
- return; //new text is not number.
- }
- pos++;
- }
- try
- {
- int value = boost::lexical_cast<int>(text);
- if(value < minValue)
- text = std::to_string(minValue);
- else if(value > maxValue)
- text = std::to_string(maxValue);
- }
- catch(boost::bad_lexical_cast &)
- {
- //Should never happen. Unless I missed some cases
- logGlobal->warn("Warning: failed to convert %s to number!", text);
- text = oldText;
- }
- }
- void CTextInput::activate()
- {
- CFocusable::activate();
- if (hasFocus())
- {
- #if defined(VCMI_MOBILE)
- //giveFocus();
- #else
- GH.startTextInput(pos);
- #endif
- }
- }
- void CTextInput::deactivate()
- {
- CFocusable::deactivate();
- if (hasFocus())
- {
- #if defined(VCMI_MOBILE)
- removeFocus();
- #else
- GH.stopTextInput();
- #endif
- }
- }
- void CTextInput::onFocusGot()
- {
- updateLabel();
- }
- void CTextInput::onFocusLost()
- {
- updateLabel();
- }
- void CFocusable::focusGot()
- {
- if (isActive())
- GH.startTextInput(pos);
- onFocusGot();
- }
- void CFocusable::focusLost()
- {
- if (isActive())
- GH.stopTextInput();
- onFocusLost();
- }
- CFocusable::CFocusable()
- {
- focusables.push_back(this);
- }
- CFocusable::~CFocusable()
- {
- if(hasFocus())
- inputWithFocus = nullptr;
- focusables -= this;
- }
- bool CFocusable::hasFocus() const
- {
- return inputWithFocus == this;
- }
- void CFocusable::giveFocus()
- {
- auto previousInput = inputWithFocus;
- inputWithFocus = this;
- if(previousInput)
- previousInput->focusLost();
- focusGot();
- }
- void CFocusable::moveFocus()
- {
- auto i = vstd::find(focusables, this);
- auto ourIt = i;
- for(i++; i != ourIt; i++)
- {
- if(i == focusables.end())
- i = focusables.begin();
- if(*i == this)
- return;
- if((*i)->isActive())
- {
- (*i)->giveFocus();
- break;
- }
- }
- }
- void CFocusable::removeFocus()
- {
- if(this == inputWithFocus)
- {
- inputWithFocus = nullptr;
- focusLost();
- }
- }
|