mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-26 14:27:14 +00:00

Formerly the code just casted the "ascii" value of the key event to "byte" and thus truncating the character value. Now that would be fine, if we would not allow values >= 256 in the ascii field, for example 322 for F8 which in turn resulted in a "B" added to the editable field. I just added a check for the values being in the byte range before doing the cast, which fixes this. svn-id: r48967
304 lines
7.3 KiB
C++
304 lines
7.3 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*/
|
|
|
|
#include "common/events.h"
|
|
#include "gui/editable.h"
|
|
#include "gui/GuiManager.h"
|
|
|
|
namespace GUI {
|
|
|
|
EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h, uint32 cmd)
|
|
: Widget(boss, x, y, w, h), CommandSender(boss), _cmd(cmd) {
|
|
init();
|
|
}
|
|
|
|
EditableWidget::EditableWidget(GuiObject *boss, const String &name, uint32 cmd)
|
|
: Widget(boss, name), CommandSender(boss), _cmd(cmd) {
|
|
init();
|
|
}
|
|
|
|
void EditableWidget::init() {
|
|
_caretVisible = false;
|
|
_caretTime = 0;
|
|
_caretPos = 0;
|
|
|
|
_caretInverse = false;
|
|
|
|
_editScrollOffset = 0;
|
|
|
|
_font = ThemeEngine::kFontStyleBold;
|
|
}
|
|
|
|
EditableWidget::~EditableWidget() {
|
|
}
|
|
|
|
void EditableWidget::reflowLayout() {
|
|
Widget::reflowLayout();
|
|
|
|
_editScrollOffset = g_gui.getStringWidth(_editString, _font) - getEditRect().width();
|
|
if (_editScrollOffset < 0)
|
|
_editScrollOffset = 0;
|
|
}
|
|
|
|
void EditableWidget::setEditString(const String &str) {
|
|
// TODO: We probably should filter the input string here,
|
|
// e.g. using tryInsertChar.
|
|
_editString = str;
|
|
_caretPos = _editString.size();
|
|
}
|
|
|
|
bool EditableWidget::tryInsertChar(byte c, int pos) {
|
|
if ((c >= 32 && c <= 127) || c >= 160) {
|
|
_editString.insertChar(c, pos);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EditableWidget::handleTickle() {
|
|
uint32 time = g_system->getMillis();
|
|
if (_caretTime < time) {
|
|
_caretTime = time + kCaretBlinkTime;
|
|
drawCaret(_caretVisible);
|
|
}
|
|
}
|
|
|
|
bool EditableWidget::handleKeyDown(Common::KeyState state) {
|
|
bool handled = true;
|
|
bool dirty = false;
|
|
bool forcecaret = false;
|
|
|
|
// First remove caret
|
|
if (_caretVisible)
|
|
drawCaret(true);
|
|
|
|
switch (state.keycode) {
|
|
case Common::KEYCODE_RETURN:
|
|
case Common::KEYCODE_KP_ENTER:
|
|
// confirm edit and exit editmode
|
|
endEditMode();
|
|
dirty = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_ESCAPE:
|
|
abortEditMode();
|
|
dirty = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_BACKSPACE:
|
|
if (_caretPos > 0) {
|
|
_caretPos--;
|
|
_editString.deleteChar(_caretPos);
|
|
dirty = true;
|
|
|
|
sendCommand(_cmd, 0);
|
|
}
|
|
forcecaret = true;
|
|
break;
|
|
|
|
// Keypad & special keys
|
|
// - if num lock is set, we always go to the default case
|
|
// - if num lock is not set, we either fall down to the special key case
|
|
// or ignore the key press in case of 0 (INSERT), 2 (DOWN), 3 (PGDWN)
|
|
// 5, 8 (UP) and 9 (PGUP)
|
|
|
|
case Common::KEYCODE_KP0:
|
|
case Common::KEYCODE_KP2:
|
|
case Common::KEYCODE_KP3:
|
|
case Common::KEYCODE_KP5:
|
|
case Common::KEYCODE_KP8:
|
|
case Common::KEYCODE_KP9:
|
|
if (state.flags & Common::KBD_NUM)
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
|
|
case Common::KEYCODE_KP_PERIOD:
|
|
if (state.flags & Common::KBD_NUM) {
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
}
|
|
case Common::KEYCODE_DELETE:
|
|
if (_caretPos < (int)_editString.size()) {
|
|
_editString.deleteChar(_caretPos);
|
|
dirty = true;
|
|
|
|
sendCommand(_cmd, 0);
|
|
}
|
|
forcecaret = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_KP1:
|
|
if (state.flags & Common::KBD_NUM) {
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
}
|
|
case Common::KEYCODE_END:
|
|
dirty = setCaretPos(_editString.size());
|
|
forcecaret = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_KP4:
|
|
if (state.flags & Common::KBD_NUM) {
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
}
|
|
case Common::KEYCODE_LEFT:
|
|
if (_caretPos > 0) {
|
|
dirty = setCaretPos(_caretPos - 1);
|
|
}
|
|
forcecaret = true;
|
|
dirty = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_KP6:
|
|
if (state.flags & Common::KBD_NUM) {
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
}
|
|
case Common::KEYCODE_RIGHT:
|
|
if (_caretPos < (int)_editString.size()) {
|
|
dirty = setCaretPos(_caretPos + 1);
|
|
}
|
|
forcecaret = true;
|
|
dirty = true;
|
|
break;
|
|
|
|
case Common::KEYCODE_KP7:
|
|
if (state.flags & Common::KBD_NUM) {
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
break;
|
|
}
|
|
case Common::KEYCODE_HOME:
|
|
dirty = setCaretPos(0);
|
|
forcecaret = true;
|
|
break;
|
|
|
|
default:
|
|
defaultKeyDownHandler(state, dirty, forcecaret, handled);
|
|
}
|
|
|
|
if (dirty)
|
|
draw();
|
|
|
|
if (forcecaret)
|
|
makeCaretVisible();
|
|
|
|
return handled;
|
|
}
|
|
|
|
void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) {
|
|
if (state.ascii < 256 && tryInsertChar((byte)state.ascii, _caretPos)) {
|
|
_caretPos++;
|
|
dirty = true;
|
|
forcecaret = true;
|
|
|
|
sendCommand(_cmd, 0);
|
|
} else {
|
|
handled = false;
|
|
}
|
|
}
|
|
|
|
int EditableWidget::getCaretOffset() const {
|
|
int caretpos = 0;
|
|
for (int i = 0; i < _caretPos; i++)
|
|
caretpos += g_gui.getCharWidth(_editString[i], _font);
|
|
|
|
caretpos -= _editScrollOffset;
|
|
|
|
return caretpos;
|
|
}
|
|
|
|
void EditableWidget::drawCaret(bool erase) {
|
|
// Only draw if item is visible
|
|
if (!isVisible() || !_boss->isVisible())
|
|
return;
|
|
|
|
Common::Rect editRect = getEditRect();
|
|
|
|
int x = editRect.left;
|
|
int y = editRect.top + 1;
|
|
|
|
x += getCaretOffset();
|
|
|
|
if (y < 0 || y + editRect.height() - 2 >= _h)
|
|
return;
|
|
|
|
x += getAbsX();
|
|
y += getAbsY();
|
|
|
|
g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height() - 2), erase);
|
|
|
|
if (erase) {
|
|
if ((uint)_caretPos < _editString.size()) {
|
|
GUI::EditableWidget::String chr(_editString[_caretPos]);
|
|
int chrWidth = g_gui.getCharWidth(_editString[_caretPos], _font);
|
|
g_gui.theme()->drawText(Common::Rect(x, y, x + chrWidth, y + editRect.height() - 2), chr, _state, Graphics::kTextAlignLeft, ThemeEngine::kTextInversionNone, 0, false, _font);
|
|
}
|
|
}
|
|
|
|
_caretVisible = !erase;
|
|
}
|
|
|
|
bool EditableWidget::setCaretPos(int newPos) {
|
|
assert(newPos >= 0 && newPos <= (int)_editString.size());
|
|
_caretPos = newPos;
|
|
return adjustOffset();
|
|
}
|
|
|
|
bool EditableWidget::adjustOffset() {
|
|
// check if the caret is still within the textbox; if it isn't,
|
|
// adjust _editScrollOffset
|
|
|
|
int caretpos = getCaretOffset();
|
|
const int editWidth = getEditRect().width();
|
|
|
|
if (caretpos < 0) {
|
|
// scroll left
|
|
_editScrollOffset += caretpos;
|
|
return true;
|
|
} else if (caretpos >= editWidth) {
|
|
// scroll right
|
|
_editScrollOffset -= (editWidth - caretpos);
|
|
return true;
|
|
} else if (_editScrollOffset > 0) {
|
|
const int strWidth = g_gui.getStringWidth(_editString, _font);
|
|
if (strWidth - _editScrollOffset < editWidth) {
|
|
// scroll right
|
|
_editScrollOffset = (strWidth - editWidth);
|
|
if (_editScrollOffset < 0)
|
|
_editScrollOffset = 0;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void EditableWidget::makeCaretVisible() {
|
|
_caretTime = g_system->getMillis() + kCaretBlinkTime;
|
|
_caretVisible = true;
|
|
drawCaret(false);
|
|
}
|
|
|
|
} // End of namespace GUI
|