| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- #include "TextBox.h"
- #include "Game.h"
- namespace gameplay
- {
- static bool space(char c)
- {
- return isspace(c);
- }
- TextBox::TextBox() : _caretLocation(0), _lastKeypress(0), _fontSize(0), _caretImage(NULL), _passwordChar('*'), _inputMode(TEXT), _ctrlPressed(false)
- {
- _canFocus = true;
- }
- TextBox::~TextBox()
- {
- }
- TextBox* TextBox::create(const char* id, Theme::Style* style)
- {
- GP_ASSERT(style);
- TextBox* textBox = new TextBox();
- if (id)
- textBox->_id = id;
- textBox->setStyle(style);
- return textBox;
- }
- Control* TextBox::create(Theme::Style* style, Properties* properties)
- {
- TextBox* textBox = new TextBox();
- textBox->initialize(style, properties);
- return textBox;
- }
- void TextBox::initialize(Theme::Style* style, Properties* properties)
- {
- GP_ASSERT(properties);
- Label::initialize(style, properties);
- _inputMode = getInputMode(properties->getString("inputMode"));
- }
- int TextBox::getLastKeypress()
- {
- return _lastKeypress;
- }
- unsigned int TextBox::getCaretLocation() const
- {
- return _caretLocation;
- }
- void TextBox::setCaretLocation(unsigned int index)
- {
- _caretLocation = index;
- if (_caretLocation > _text.length())
- _caretLocation = (unsigned int)_text.length();
- }
- bool TextBox::touchEvent(Touch::TouchEvent evt, int x, int y, unsigned int contactIndex)
- {
- State state = getState();
- switch (evt)
- {
- case Touch::TOUCH_PRESS:
- if (state == ACTIVE)
- {
- setCaretLocation(x, y);
- _dirty = true;
- }
- break;
- case Touch::TOUCH_MOVE:
- if (state == ACTIVE)
- {
- setCaretLocation(x, y);
- _dirty = true;
- }
- break;
- }
- return Label::touchEvent(evt, x, y, contactIndex);
- }
- static bool isWhitespace(char c)
- {
- switch (c)
- {
- case ' ':
- case '\t':
- case '\r':
- case '\n':
- return true;
- default:
- return false;
- }
- }
- static unsigned int findNextWord(const std::string& text, unsigned int from, bool backwards)
- {
- int pos = (int)from;
- if (backwards)
- {
- if (pos > 0)
- {
- // Moving backwards: skip all consecutive whitespace characters
- while (pos > 0 && isWhitespace(text.at(pos-1)))
- --pos;
- // Now search back to the first whitespace character
- while (pos > 0 && !isWhitespace(text.at(pos-1)))
- --pos;
- }
- }
- else
- {
- const int len = (const int)text.length();
- if (pos < len)
- {
- // Moving forward: skip all consecutive non-whitespace characters
- ++pos;
- while (pos < len && !isWhitespace(text.at(pos)))
- ++pos;
- // Now search for the first non-whitespace character
- while (pos < len && isWhitespace(text.at(pos)))
- ++pos;
- }
- }
- return (unsigned int)pos;
- }
- bool TextBox::keyEvent(Keyboard::KeyEvent evt, int key)
- {
- switch (evt)
- {
- case Keyboard::KEY_PRESS:
- {
- switch (key)
- {
- case Keyboard::KEY_CTRL:
- {
- _ctrlPressed = true;
- break;
- }
- case Keyboard::KEY_HOME:
- {
- _caretLocation = 0;
- _dirty = true;
- break;
- }
- case Keyboard::KEY_END:
- {
- _caretLocation = _text.length();
- _dirty = true;
- break;
- }
- case Keyboard::KEY_DELETE:
- {
- if (_caretLocation < _text.length())
- {
- int newCaretLocation;
- if (_ctrlPressed)
- {
- newCaretLocation = findNextWord(getDisplayedText(), _caretLocation, false);
- }
- else
- {
- newCaretLocation = _caretLocation + 1;
- }
- _text.erase(_caretLocation, newCaretLocation - _caretLocation);
- _dirty = true;
- notifyListeners(Control::Listener::TEXT_CHANGED);
- }
- break;
- }
- case Keyboard::KEY_TAB:
- {
- // Allow tab to move the focus forward.
- return false;
- }
- case Keyboard::KEY_LEFT_ARROW:
- {
- if (_caretLocation > 0)
- {
- if (_ctrlPressed)
- {
- _caretLocation = findNextWord(getDisplayedText(), _caretLocation, true);
- }
- else
- {
- --_caretLocation;
- }
- }
- _dirty = true;
- break;
- }
- case Keyboard::KEY_RIGHT_ARROW:
- {
- if (_caretLocation < _text.length())
- {
- if (_ctrlPressed)
- {
- _caretLocation = findNextWord(getDisplayedText(), _caretLocation, false);
- }
- else
- {
- ++_caretLocation;
- }
- }
- _dirty = true;
- break;
- }
- case Keyboard::KEY_UP_ARROW:
- {
- // TODO: Support multiline
- break;
- }
- case Keyboard::KEY_DOWN_ARROW:
- {
- // TODO: Support multiline
- break;
- }
- case Keyboard::KEY_BACKSPACE:
- {
- if (_caretLocation > 0)
- {
- int newCaretLocation;
- if (_ctrlPressed)
- {
- newCaretLocation = findNextWord(getDisplayedText(), _caretLocation, true);
- }
- else
- {
- newCaretLocation = _caretLocation - 1;
- }
- _text.erase(newCaretLocation, _caretLocation - newCaretLocation);
- _caretLocation = newCaretLocation;
- _dirty = true;
- notifyListeners(Control::Listener::TEXT_CHANGED);
- }
- break;
- }
- }
- break;
- }
- case Keyboard::KEY_CHAR:
- {
- switch (key)
- {
- case Keyboard::KEY_RETURN:
- // TODO: Support multi-line
- break;
- case Keyboard::KEY_ESCAPE:
- break;
- case Keyboard::KEY_BACKSPACE:
- break;
- case Keyboard::KEY_TAB:
- // Allow tab to move the focus forward.
- return false;
- default:
- {
- // Insert character into string, only if our font supports this character
- if (_font && _font->isCharacterSupported(key))
- {
- if (_caretLocation <= _text.length())
- {
- _text.insert(_caretLocation, 1, (char)key);
- ++_caretLocation;
- }
- _dirty = true;
- notifyListeners(Control::Listener::TEXT_CHANGED);
- }
- break;
- }
-
- break;
- }
- break;
- }
- case Keyboard::KEY_RELEASE:
- switch (key)
- {
- case Keyboard::KEY_CTRL:
- {
- _ctrlPressed = false;
- break;
- }
- }
- }
- _lastKeypress = key;
- return Label::keyEvent(evt, key);
- }
- void TextBox::controlEvent(Control::Listener::EventType evt)
- {
- Label::controlEvent(evt);
- switch (evt)
- {
- case Control::Listener::FOCUS_GAINED:
- Game::getInstance()->displayKeyboard(true);
- break;
- case Control::Listener::FOCUS_LOST:
- Game::getInstance()->displayKeyboard(false);
- break;
- }
- }
- void TextBox::update(const Control* container, const Vector2& offset)
- {
- Label::update(container, offset);
- Control::State state = getState();
- _fontSize = getFontSize(state);
- _caretImage = getImage("textCaret", state);
- }
- void TextBox::drawImages(SpriteBatch* spriteBatch, const Rectangle& clip)
- {
- Control::State state = getState();
- if (_caretImage && (state == ACTIVE || hasFocus()))
- {
- // Draw the cursor at its current location.
- const Rectangle& region = _caretImage->getRegion();
- if (!region.isEmpty())
- {
- GP_ASSERT(spriteBatch);
- const Theme::UVs uvs = _caretImage->getUVs();
- Vector4 color = _caretImage->getColor();
- color.w *= _opacity;
- float caretWidth = region.width * _fontSize / region.height;
- Font* font = getFont(state);
- unsigned int fontSize = getFontSize(state);
- Vector2 point;
- font->getLocationAtIndex(getDisplayedText().c_str(), _textBounds, fontSize, &point, _caretLocation,
- getTextAlignment(state), true, getTextRightToLeft(state));
- spriteBatch->draw(point.x - caretWidth * 0.5f, point.y, caretWidth, fontSize, uvs.u1, uvs.v1, uvs.u2, uvs.v2, color, _viewportClipBounds);
- }
- }
- _dirty = false;
- }
- void TextBox::setCaretLocation(int x, int y)
- {
- Control::State state = getState();
- Vector2 point(x + _absoluteBounds.x, y + _absoluteBounds.y);
- // Get index into string and cursor location from the latest touch location.
- Font* font = getFont(state);
- unsigned int fontSize = getFontSize(state);
- Font::Justify textAlignment = getTextAlignment(state);
- bool rightToLeft = getTextRightToLeft(state);
- const std::string displayedText = getDisplayedText();
- int index = font->getIndexAtLocation(displayedText.c_str(), _textBounds, fontSize, point, &point,
- textAlignment, true, rightToLeft);
- if (index == -1)
- {
- // Attempt to find the nearest valid caret location.
- Rectangle textBounds;
- font->measureText(displayedText.c_str(), _textBounds, fontSize, &textBounds, textAlignment, true, true);
- if (point.x > textBounds.x + textBounds.width &&
- point.y > textBounds.y + textBounds.height)
- {
- font->getLocationAtIndex(displayedText.c_str(), _textBounds, fontSize, &point, (unsigned int)_text.length(),
- textAlignment, true, rightToLeft);
- return;
- }
- if (point.x < textBounds.x)
- {
- point.x = textBounds.x;
- }
- else if (point.x > textBounds.x + textBounds.width)
- {
- point.x = textBounds.x + textBounds.width;
- }
- if (point.y < textBounds.y)
- {
- point.y = textBounds.y;
- }
- else if (point.y > textBounds.y + textBounds.height)
- {
- Font* font = getFont(state);
- GP_ASSERT(font);
- unsigned int fontSize = getFontSize(state);
- point.y = textBounds.y + textBounds.height - fontSize;
- }
- index = font->getIndexAtLocation(displayedText.c_str(), _textBounds, fontSize, point, &point,
- textAlignment, true, rightToLeft);
- }
- if (index != -1)
- _caretLocation = index;
- }
- void TextBox::getCaretLocation(Vector2* p)
- {
- GP_ASSERT(p);
- State state = getState();
- getFont(state)->getLocationAtIndex(getDisplayedText().c_str(), _textBounds, getFontSize(state), p, _caretLocation, getTextAlignment(state), true, getTextRightToLeft(state));
- }
- const char* TextBox::getType() const
- {
- return "textBox";
- }
- void TextBox::setPasswordChar(char character)
- {
- _passwordChar = character;
- }
- char TextBox::getPasswordChar() const
- {
- return _passwordChar;
- }
- void TextBox::setInputMode(InputMode inputMode)
- {
- _inputMode = inputMode;
- }
- TextBox::InputMode TextBox::getInputMode() const
- {
- return _inputMode;
- }
- void TextBox::drawText(const Rectangle& clip)
- {
- if (_text.size() <= 0)
- return;
- // Draw the text.
- if (_font)
- {
- Control::State state = getState();
- const std::string displayedText = getDisplayedText();
- _font->start();
- _font->drawText(displayedText.c_str(), _textBounds, _textColor, getFontSize(state), getTextAlignment(state), true, getTextRightToLeft(state), &_viewportClipBounds);
- _font->finish();
- }
- }
- TextBox::InputMode TextBox::getInputMode(const char* inputMode)
- {
- if (!inputMode)
- {
- return TextBox::TEXT;
- }
- if (strcmp(inputMode, "TEXT") == 0)
- {
- return TextBox::TEXT;
- }
- else if (strcmp(inputMode, "PASSWORD") == 0)
- {
- return TextBox::PASSWORD;
- }
- else
- {
- GP_ERROR("Failed to get corresponding textbox inputmode for unsupported value '%s'.", inputMode);
- }
- // Default.
- return TextBox::TEXT;
- }
- std::string TextBox::getDisplayedText() const
- {
- std::string displayedText;
- switch (_inputMode) {
- case PASSWORD:
- displayedText.insert((size_t)0, _text.length(), _passwordChar);
- break;
- case TEXT:
- default:
- displayedText = _text;
- break;
- }
- return displayedText;
- }
- }
|