mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-19 08:25:35 +00:00
Move more text editing code into class EditableWidget; ListWidget now has all the editing capabilities of EditTextWidget
svn-id: r16694
This commit is contained in:
parent
66c524f0ee
commit
b43a53f74d
11
TODO
11
TODO
@ -194,11 +194,12 @@ Files
|
||||
GUI
|
||||
===
|
||||
* Remove hard coded 320x200 assumptions, use game screen size
|
||||
* Add ability to scale GUI (ie. to make the GUI less tiny in COMI)
|
||||
* Remove code duplication between EditTextWidget and ListWidget (i.e. text
|
||||
editing code; maybe we can factor that out into a common base or aggregate
|
||||
class... not yet sure).
|
||||
* Fix EditTextWidget::drawCaret and ListWidget::drawCaret support for alternate
|
||||
* EditableWidget: Make it possible to specify a min/max length for the text
|
||||
* EditableWidget: Let setEditString filter the string it gets
|
||||
* EditableWidget: Right now, custom filtering requires the user to subclass;
|
||||
it would be nice if there was simply a "validator hook" or so.
|
||||
Maybe take some inspiration from Java's Swing in this matter.
|
||||
* Improve EditTextWidget::drawCaret and ListWidget::drawCaret support for alternate
|
||||
fonts (the current code overdraws chars partly, and relies on the fact that
|
||||
our default built-in font has a separation pixel column on the *left* side;
|
||||
most other bitmap fonts have it on the right, though). To this end, we maybe
|
||||
|
@ -28,16 +28,15 @@ namespace GUI {
|
||||
|
||||
EditTextWidget::EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text)
|
||||
: EditableWidget(boss, x, y - 1, w, h + 2) {
|
||||
_editString = text;
|
||||
_backupString = text;
|
||||
_flags = WIDGET_ENABLED | WIDGET_CLEARBG | WIDGET_RETAIN_FOCUS | WIDGET_WANT_TICKLE;
|
||||
_type = kEditTextWidget;
|
||||
|
||||
_caretPos = _editString.size();
|
||||
setEditString(text);
|
||||
}
|
||||
|
||||
_editScrollOffset = (g_gui.getStringWidth(_editString) - (getEditRect().width()));
|
||||
if (_editScrollOffset < 0)
|
||||
_editScrollOffset = 0;
|
||||
void EditTextWidget::setEditString(const String &str) {
|
||||
EditableWidget::setEditString(str);
|
||||
_backupString = str;
|
||||
}
|
||||
|
||||
void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) {
|
||||
@ -61,75 +60,6 @@ void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) {
|
||||
draw();
|
||||
}
|
||||
|
||||
bool EditTextWidget::tryInsertChar(char c, int pos) {
|
||||
if (isprint(c)) {
|
||||
_editString.insertChar(c, pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool EditTextWidget::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
|
||||
bool handled = true;
|
||||
bool dirty = false;
|
||||
|
||||
// First remove caret
|
||||
if (_caretVisible)
|
||||
drawCaret(true);
|
||||
|
||||
switch (keycode) {
|
||||
case '\n': // enter/return
|
||||
case '\r':
|
||||
// confirm edit and exit editmode
|
||||
endEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 27: // escape
|
||||
abortEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 8: // backspace
|
||||
if (_caretPos > 0) {
|
||||
_caretPos--;
|
||||
_editString.deleteChar(_caretPos);
|
||||
}
|
||||
dirty = true;
|
||||
break;
|
||||
case 127: // delete
|
||||
_editString.deleteChar(_caretPos);
|
||||
dirty = true;
|
||||
break;
|
||||
case 256 + 20: // left arrow
|
||||
if (_caretPos > 0) {
|
||||
dirty = setCaretPos(_caretPos - 1);
|
||||
}
|
||||
break;
|
||||
case 256 + 19: // right arrow
|
||||
if (_caretPos < (int)_editString.size()) {
|
||||
dirty = setCaretPos(_caretPos + 1);
|
||||
}
|
||||
break;
|
||||
case 256 + 22: // home
|
||||
dirty = setCaretPos(0);
|
||||
break;
|
||||
case 256 + 23: // end
|
||||
dirty = setCaretPos(_editString.size());
|
||||
break;
|
||||
default:
|
||||
if (tryInsertChar((char)ascii, _caretPos)) {
|
||||
_caretPos++;
|
||||
dirty = true;
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
draw();
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
void EditTextWidget::drawWidget(bool hilite) {
|
||||
// Draw a thin frame around us.
|
||||
@ -149,16 +79,6 @@ Common::Rect EditTextWidget::getEditRect() const {
|
||||
return r;
|
||||
}
|
||||
|
||||
int EditTextWidget::getCaretOffset() const {
|
||||
int caretpos = 0;
|
||||
for (int i = 0; i < _caretPos; i++)
|
||||
caretpos += g_gui.getCharWidth(_editString[i]);
|
||||
|
||||
caretpos -= _editScrollOffset;
|
||||
|
||||
return caretpos;
|
||||
}
|
||||
|
||||
void EditTextWidget::receivedFocusWidget() {
|
||||
}
|
||||
|
||||
@ -176,46 +96,8 @@ void EditTextWidget::endEditMode() {
|
||||
}
|
||||
|
||||
void EditTextWidget::abortEditMode() {
|
||||
_editString = _backupString;
|
||||
_caretPos = _editString.size();
|
||||
_editScrollOffset = (g_gui.getStringWidth(_editString) - (getEditRect().width()));
|
||||
if (_editScrollOffset < 0)
|
||||
_editScrollOffset = 0;
|
||||
setEditString(_backupString);
|
||||
releaseFocus();
|
||||
}
|
||||
|
||||
bool EditTextWidget::setCaretPos(int newPos) {
|
||||
assert(newPos >= 0 && newPos <= (int)_editString.size());
|
||||
_caretPos = newPos;
|
||||
return adjustOffset();
|
||||
}
|
||||
|
||||
bool EditTextWidget::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);
|
||||
if (strWidth - _editScrollOffset < editWidth) {
|
||||
// scroll right
|
||||
_editScrollOffset = (strWidth - editWidth);
|
||||
if (_editScrollOffset < 0)
|
||||
_editScrollOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // End of namespace GUI
|
||||
|
@ -36,11 +36,9 @@ protected:
|
||||
public:
|
||||
EditTextWidget(GuiObject *boss, int x, int y, int w, int h, const String &text);
|
||||
|
||||
// void setString(const String &str) { _editString = str; }
|
||||
const String &getString() const { return _editString; }
|
||||
void setEditString(const String &str);
|
||||
|
||||
virtual void handleMouseDown(int x, int y, int button, int clickCount);
|
||||
virtual bool handleKeyDown(uint16 ascii, int keycode, int modifiers);
|
||||
|
||||
virtual bool wantsFocus() { return true; };
|
||||
|
||||
@ -54,11 +52,6 @@ protected:
|
||||
void abortEditMode();
|
||||
|
||||
Common::Rect getEditRect() const;
|
||||
int getCaretOffset() const;
|
||||
bool setCaretPos(int newPos);
|
||||
bool adjustOffset();
|
||||
|
||||
virtual bool tryInsertChar(char c, int pos);
|
||||
};
|
||||
|
||||
} // End of namespace GUI
|
||||
|
@ -124,7 +124,7 @@ void ListWidget::handleMouseDown(int x, int y, int button, int clickCount) {
|
||||
}
|
||||
|
||||
// TODO: Determine where inside the string the user clicked and place the
|
||||
// caret accordingly.
|
||||
// caret accordingly. See _editScrollOffset and EditTextWidget::handleMouseDown.
|
||||
draw();
|
||||
|
||||
}
|
||||
@ -193,35 +193,8 @@ bool ListWidget::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
|
||||
|
||||
scrollToCurrent();
|
||||
} else if (_editMode) {
|
||||
|
||||
if (_caretVisible)
|
||||
drawCaret(true);
|
||||
|
||||
switch (keycode) {
|
||||
case '\n': // enter/return
|
||||
case '\r':
|
||||
// confirm edit and exit editmode
|
||||
endEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 27: // escape
|
||||
// abort edit and exit editmode
|
||||
abortEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 8: // backspace
|
||||
_editString.deleteLastChar();
|
||||
dirty = true;
|
||||
break;
|
||||
default:
|
||||
if (isprint((char)ascii)) {
|
||||
_editString += (char)ascii;
|
||||
dirty = true;
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Class EditableWidget handles all text editing related key presses for us
|
||||
handled = EditableWidget::handleKeyDown(ascii, keycode, modifiers);
|
||||
} else {
|
||||
// not editmode
|
||||
|
||||
@ -313,6 +286,7 @@ void ListWidget::drawWidget(bool hilite) {
|
||||
NewGui *gui = &g_gui;
|
||||
int i, pos, len = _list.size();
|
||||
Common::String buffer;
|
||||
int offset, deltax;
|
||||
|
||||
// Draw a thin frame around the list.
|
||||
gui->hLine(_x, _y, _x + _w - 1, gui->_color);
|
||||
@ -321,26 +295,41 @@ void ListWidget::drawWidget(bool hilite) {
|
||||
|
||||
// Draw the list items
|
||||
for (i = 0, pos = _currentPos; i < _entriesPerPage && pos < len; i++, pos++) {
|
||||
if (_numberingMode != kListNumberingOff) {
|
||||
char temp[10];
|
||||
sprintf(temp, "%2d. ", (pos + _numberingMode));
|
||||
buffer = temp;
|
||||
} else {
|
||||
buffer.clear();
|
||||
}
|
||||
if (_selectedItem == pos && _editMode)
|
||||
buffer += _editString;
|
||||
else
|
||||
buffer += _list[pos];
|
||||
const OverlayColor textColor = (_selectedItem == pos && _hasFocus) ? gui->_bgcolor : gui->_textcolor;
|
||||
const int y = _y + 2 + kLineHeight * i;
|
||||
|
||||
// Draw the selected item inverted, on a highlighted background.
|
||||
if (_selectedItem == pos) {
|
||||
if (_hasFocus)
|
||||
gui->fillRect(_x + 1, _y + 1 + kLineHeight * i, _w - 1, kLineHeight, gui->_textcolorhi);
|
||||
else
|
||||
gui->frameRect(_x + 1, _y + 1 + kLineHeight * i, _w - 1, kLineHeight, gui->_textcolorhi);
|
||||
}
|
||||
gui->drawString(buffer, _x + 2, _y + 2 + kLineHeight * i, _w - 4,
|
||||
(_selectedItem == pos && _hasFocus) ? gui->_bgcolor : gui->_textcolor);
|
||||
|
||||
// If in numbering mode, we first print a number prefix
|
||||
if (_numberingMode != kListNumberingOff) {
|
||||
char temp[10];
|
||||
sprintf(temp, "%2d. ", (pos + _numberingMode));
|
||||
buffer = temp;
|
||||
gui->drawString(buffer, _x + 2, y, _w - 4, textColor);
|
||||
offset = gui->getStringWidth(buffer);
|
||||
} else {
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
Common::Rect r(getEditRect());
|
||||
if (_selectedItem == pos && _editMode) {
|
||||
|
||||
buffer = _editString;
|
||||
adjustOffset();
|
||||
deltax = -_editScrollOffset;
|
||||
|
||||
gui->drawString(buffer, _x + r.left, y, r.width(), textColor, kTextAlignLeft, deltax, false);
|
||||
} else {
|
||||
buffer = _list[pos];
|
||||
deltax = 0;
|
||||
gui->drawString(buffer, _x + r.left, y, r.width(), textColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,14 +348,6 @@ Common::Rect ListWidget::getEditRect() const {
|
||||
return r;
|
||||
}
|
||||
|
||||
int ListWidget::getCaretOffset() const {
|
||||
int caretpos = 0;
|
||||
|
||||
caretpos += g_gui.getStringWidth(_editString);
|
||||
|
||||
return caretpos;
|
||||
}
|
||||
|
||||
void ListWidget::scrollToCurrent() {
|
||||
// Only do something if the current item is not in our view port
|
||||
if (_selectedItem < _currentPos) {
|
||||
@ -389,8 +370,7 @@ void ListWidget::scrollToCurrent() {
|
||||
void ListWidget::startEditMode() {
|
||||
if (_editable && !_editMode && _selectedItem >= 0) {
|
||||
_editMode = true;
|
||||
_editString = _list[_selectedItem];
|
||||
_caretPos = _editString.size();
|
||||
setEditString(_list[_selectedItem]);
|
||||
draw();
|
||||
}
|
||||
}
|
||||
|
@ -96,7 +96,6 @@ protected:
|
||||
void abortEditMode();
|
||||
|
||||
Common::Rect getEditRect() const;
|
||||
int getCaretOffset() const;
|
||||
|
||||
void lostFocusWidget();
|
||||
void scrollToCurrent();
|
||||
|
126
gui/editable.cpp
126
gui/editable.cpp
@ -1,5 +1,5 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2002-2005 The ScummVM project
|
||||
* Copyright (C) 2005 The ScummVM project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -39,6 +39,25 @@ EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h)
|
||||
EditableWidget::~EditableWidget() {
|
||||
}
|
||||
|
||||
void EditableWidget::setEditString(const String &str) {
|
||||
// TODO: We probably should filter the input string here,
|
||||
// e.g. using tryInsertChar.
|
||||
_editString = str;
|
||||
_caretPos = _editString.size();
|
||||
|
||||
_editScrollOffset = (g_gui.getStringWidth(_editString) - (getEditRect().width()));
|
||||
if (_editScrollOffset < 0)
|
||||
_editScrollOffset = 0;
|
||||
}
|
||||
|
||||
bool EditableWidget::tryInsertChar(char c, int pos) {
|
||||
if (isprint(c)) {
|
||||
_editString.insertChar(c, pos);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void EditableWidget::handleTickle() {
|
||||
uint32 time = getMillis();
|
||||
if (_caretTime < time) {
|
||||
@ -47,6 +66,77 @@ void EditableWidget::handleTickle() {
|
||||
}
|
||||
}
|
||||
|
||||
bool EditableWidget::handleKeyDown(uint16 ascii, int keycode, int modifiers) {
|
||||
bool handled = true;
|
||||
bool dirty = false;
|
||||
|
||||
// First remove caret
|
||||
if (_caretVisible)
|
||||
drawCaret(true);
|
||||
|
||||
switch (keycode) {
|
||||
case '\n': // enter/return
|
||||
case '\r':
|
||||
// confirm edit and exit editmode
|
||||
endEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 27: // escape
|
||||
abortEditMode();
|
||||
dirty = true;
|
||||
break;
|
||||
case 8: // backspace
|
||||
if (_caretPos > 0) {
|
||||
_caretPos--;
|
||||
_editString.deleteChar(_caretPos);
|
||||
dirty = true;
|
||||
}
|
||||
break;
|
||||
case 127: // delete
|
||||
_editString.deleteChar(_caretPos);
|
||||
dirty = true;
|
||||
break;
|
||||
case 256 + 20: // left arrow
|
||||
if (_caretPos > 0) {
|
||||
dirty = setCaretPos(_caretPos - 1);
|
||||
}
|
||||
break;
|
||||
case 256 + 19: // right arrow
|
||||
if (_caretPos < (int)_editString.size()) {
|
||||
dirty = setCaretPos(_caretPos + 1);
|
||||
}
|
||||
break;
|
||||
case 256 + 22: // home
|
||||
dirty = setCaretPos(0);
|
||||
break;
|
||||
case 256 + 23: // end
|
||||
dirty = setCaretPos(_editString.size());
|
||||
break;
|
||||
default:
|
||||
if (tryInsertChar((char)ascii, _caretPos)) {
|
||||
_caretPos++;
|
||||
dirty = true;
|
||||
} else {
|
||||
handled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
draw();
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
int EditableWidget::getCaretOffset() const {
|
||||
int caretpos = 0;
|
||||
for (int i = 0; i < _caretPos; i++)
|
||||
caretpos += g_gui.getCharWidth(_editString[i]);
|
||||
|
||||
caretpos -= _editScrollOffset;
|
||||
|
||||
return caretpos;
|
||||
}
|
||||
|
||||
void EditableWidget::drawCaret(bool erase) {
|
||||
// Only draw if item is visible
|
||||
if (!isVisible() || !_boss->isVisible())
|
||||
@ -70,5 +160,39 @@ void EditableWidget::drawCaret(bool erase) {
|
||||
_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);
|
||||
if (strWidth - _editScrollOffset < editWidth) {
|
||||
// scroll right
|
||||
_editScrollOffset = (strWidth - editWidth);
|
||||
if (_editScrollOffset < 0)
|
||||
_editScrollOffset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
} // End of namespace GUI
|
||||
|
@ -1,5 +1,5 @@
|
||||
/* ScummVM - Scumm Interpreter
|
||||
* Copyright (C) 2002-2005 The ScummVM project
|
||||
* Copyright (C) 2005 The ScummVM project
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -27,7 +27,10 @@
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
/**
|
||||
* Base class for widgets which need to edit text, like ListWidget and
|
||||
* EditTextWidget.
|
||||
*/
|
||||
class EditableWidget : public Widget {
|
||||
public:
|
||||
typedef Common::String String;
|
||||
@ -46,7 +49,11 @@ public:
|
||||
EditableWidget(GuiObject *boss, int x, int y, int w, int h);
|
||||
virtual ~EditableWidget();
|
||||
|
||||
virtual void setEditString(const String &str);
|
||||
virtual const String &getEditString() const { return _editString; }
|
||||
|
||||
virtual void handleTickle();
|
||||
virtual bool handleKeyDown(uint16 ascii, int keycode, int modifiers);
|
||||
|
||||
protected:
|
||||
virtual void startEditMode() = 0;
|
||||
@ -54,8 +61,12 @@ protected:
|
||||
virtual void abortEditMode() = 0;
|
||||
|
||||
virtual Common::Rect getEditRect() const = 0;
|
||||
virtual int getCaretOffset() const = 0;
|
||||
virtual int getCaretOffset() const;
|
||||
void drawCaret(bool erase);
|
||||
bool setCaretPos(int newPos);
|
||||
bool adjustOffset();
|
||||
|
||||
virtual bool tryInsertChar(char c, int pos);
|
||||
};
|
||||
|
||||
} // End of namespace GUI
|
||||
|
@ -303,7 +303,7 @@ void EditGameDialog::open() {
|
||||
|
||||
void EditGameDialog::close() {
|
||||
if (getResult()) {
|
||||
ConfMan.set("description", _descriptionWidget->getString(), _domain);
|
||||
ConfMan.set("description", _descriptionWidget->getEditString(), _domain);
|
||||
|
||||
Common::Language lang = (Common::Language)_langPopUp->getSelectedTag();
|
||||
if (lang < 0)
|
||||
@ -389,7 +389,7 @@ void EditGameDialog::handleCommand(CommandSender *sender, uint32 cmd, uint32 dat
|
||||
|
||||
case kOKCmd: {
|
||||
// Write back changes made to config object
|
||||
String newDomain(_domainWidget->getString());
|
||||
String newDomain(_domainWidget->getEditString());
|
||||
if (newDomain != _domain) {
|
||||
if (newDomain.isEmpty() || ConfMan.hasGameDomain(newDomain)) {
|
||||
MessageDialog alert("This game ID is already taken. Please choose another one.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user