GUI: Support unicode and BiDi in editable widget

This commit is contained in:
BLooperZ 2022-07-29 01:46:16 +03:00 committed by Eugene Sandulenko
parent 14e61b0590
commit b67b88e6d9
6 changed files with 46 additions and 25 deletions

View file

@ -1665,11 +1665,11 @@ int ThemeEngine::getStringWidth(const Common::U32String &str, FontStyle font) co
return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getStringWidth(str) : 0; return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getStringWidth(str) : 0;
} }
int ThemeEngine::getCharWidth(byte c, FontStyle font) const { int ThemeEngine::getCharWidth(uint32 c, FontStyle font) const {
return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getCharWidth(c) : 0; return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getCharWidth(c) : 0;
} }
int ThemeEngine::getKerningOffset(byte left, byte right, FontStyle font) const { int ThemeEngine::getKerningOffset(uint32 left, uint32 right, FontStyle font) const {
return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getKerningOffset(left, right) : 0; return ready() ? _texts[fontStyleToData(font)]->_fontPtr->getKerningOffset(left, right) : 0;
} }

View file

@ -432,9 +432,9 @@ public:
int getStringWidth(const Common::U32String &str, FontStyle font = kFontStyleBold) const; int getStringWidth(const Common::U32String &str, FontStyle font = kFontStyleBold) const;
int getCharWidth(byte c, FontStyle font = kFontStyleBold) const; int getCharWidth(uint32 c, FontStyle font = kFontStyleBold) const;
int getKerningOffset(byte left, byte right, FontStyle font = kFontStyleBold) const; int getKerningOffset(uint32 left, uint32 right, FontStyle font = kFontStyleBold) const;
//@} //@}

View file

@ -92,7 +92,7 @@ public:
: EditTextWidget(boss, name, text, tooltip) {} : EditTextWidget(boss, name, text, tooltip) {}
protected: protected:
bool tryInsertChar(byte c, int pos) override { bool tryInsertChar(Common::u32char_type_t c, int pos) override {
if (Common::isAlnum(c) || c == '-' || c == '_') { if (Common::isAlnum(c) || c == '-' || c == '_') {
_editString.insertChar(c, pos); _editString.insertChar(c, pos);
return true; return true;

View file

@ -115,8 +115,8 @@ public:
int getFontHeight(ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getFontHeight(style); } int getFontHeight(ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getFontHeight(style); }
int getStringWidth(const Common::String &str, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getStringWidth(str, style); } int getStringWidth(const Common::String &str, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getStringWidth(str, style); }
int getStringWidth(const Common::U32String &str, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getStringWidth(str, style); } int getStringWidth(const Common::U32String &str, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getStringWidth(str, style); }
int getCharWidth(byte c, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getCharWidth(c, style); } int getCharWidth(uint32 c, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getCharWidth(c, style); }
int getKerningOffset(byte left, byte right, ThemeEngine::FontStyle font = ThemeEngine::kFontStyleBold) const { return _theme->getKerningOffset(left, right, font); } int getKerningOffset(uint32 left, uint32 right, ThemeEngine::FontStyle font = ThemeEngine::kFontStyleBold) const { return _theme->getKerningOffset(left, right, font); }
/** /**
* Tell the GuiManager to check whether the screen resolution has changed. * Tell the GuiManager to check whether the screen resolution has changed.

View file

@ -21,6 +21,7 @@
#include "common/rect.h" #include "common/rect.h"
#include "common/system.h" #include "common/system.h"
#include "common/unicode-bidi.h"
#include "gui/widgets/editable.h" #include "gui/widgets/editable.h"
#include "gui/gui-manager.h" #include "gui/gui-manager.h"
#include "graphics/font.h" #include "graphics/font.h"
@ -75,7 +76,7 @@ void EditableWidget::setEditString(const Common::U32String &str) {
_caretPos = 0; _caretPos = 0;
} }
bool EditableWidget::tryInsertChar(byte c, int pos) { bool EditableWidget::tryInsertChar(Common::u32char_type_t c, int pos) {
if ((c >= 32 && c <= 127) || c >= 160) { if ((c >= 32 && c <= 127) || c >= 160) {
_editString.insertChar(c, pos); _editString.insertChar(c, pos);
return true; return true;
@ -83,6 +84,14 @@ bool EditableWidget::tryInsertChar(byte c, int pos) {
return false; return false;
} }
int EditableWidget::caretVisualPos(int logicalPos) {
return Common::convertBiDiU32String(_editString + " ").getVisualPosition(logicalPos);
}
int EditableWidget::caretLogicalPos() const {
return Common::convertBiDiU32String(_editString + " ").getLogicalPosition(_caretPos);
}
void EditableWidget::handleTickle() { void EditableWidget::handleTickle() {
uint32 time = g_system->getMillis(); uint32 time = g_system->getMillis();
if (_caretTime < time && isEnabled()) { if (_caretTime < time && isEnabled()) {
@ -95,6 +104,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
bool handled = true; bool handled = true;
bool dirty = false; bool dirty = false;
bool forcecaret = false; bool forcecaret = false;
int deleteIndex;
if (!isEnabled()) if (!isEnabled())
return false; return false;
@ -139,9 +149,11 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break; break;
case Common::KEYCODE_BACKSPACE: case Common::KEYCODE_BACKSPACE:
if (_caretPos > 0) { deleteIndex = caretLogicalPos();
_caretPos--; if (deleteIndex > 0) {
_editString.deleteChar(_caretPos); deleteIndex--;
_editString.deleteChar(deleteIndex);
setCaretPos(caretVisualPos(deleteIndex));
dirty = true; dirty = true;
sendCommand(_cmd, 0); sendCommand(_cmd, 0);
@ -150,8 +162,10 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break; break;
case Common::KEYCODE_DELETE: case Common::KEYCODE_DELETE:
if (_caretPos < (int)_editString.size()) { deleteIndex = caretLogicalPos();
_editString.deleteChar(_caretPos); if (deleteIndex < (int)_editString.size()) {
_editString.deleteChar(deleteIndex);
setCaretPos(caretVisualPos(deleteIndex));
dirty = true; dirty = true;
sendCommand(_cmd, 0); sendCommand(_cmd, 0);
@ -162,7 +176,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_DOWN: case Common::KEYCODE_DOWN:
case Common::KEYCODE_END: case Common::KEYCODE_END:
// Move caret to end // Move caret to end
setCaretPos(_editString.size()); setCaretPos(caretVisualPos(_editString.size()));
forcecaret = true; forcecaret = true;
dirty = true; dirty = true;
break; break;
@ -188,7 +202,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_UP: case Common::KEYCODE_UP:
case Common::KEYCODE_HOME: case Common::KEYCODE_HOME:
// Move caret to start // Move caret to start
setCaretPos(0); setCaretPos(caretVisualPos(0));
forcecaret = true; forcecaret = true;
dirty = true; dirty = true;
break; break;
@ -198,8 +212,9 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
if (g_system->hasTextInClipboard()) { if (g_system->hasTextInClipboard()) {
Common::U32String text = g_system->getTextFromClipboard(); Common::U32String text = g_system->getTextFromClipboard();
for (uint32 i = 0; i < text.size(); ++i) { for (uint32 i = 0; i < text.size(); ++i) {
if (tryInsertChar(text[i], _caretPos)) const int logicalPosition = caretLogicalPos();
++_caretPos; if (tryInsertChar(text[i], logicalPosition))
setCaretPos(caretVisualPos(logicalPosition + 1));
} }
dirty = true; dirty = true;
} }
@ -261,8 +276,9 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
} }
void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) { void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) {
if (state.ascii < 256 && tryInsertChar((byte)state.ascii, _caretPos)) { const int logicalPosition = caretLogicalPos();
_caretPos++; if (tryInsertChar(state.ascii, logicalPosition)) {
setCaretPos(caretVisualPos(logicalPosition + 1));
dirty = true; dirty = true;
forcecaret = true; forcecaret = true;
@ -273,7 +289,8 @@ void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty,
} }
int EditableWidget::getCaretOffset() const { int EditableWidget::getCaretOffset() const {
Common::U32String substr(_editString.begin(), _editString.begin() + _caretPos); Common::UnicodeBiDiText utxt(_editString);
Common::U32String substr(utxt.visual.begin(), utxt.visual.begin() + _caretPos);
return g_gui.getStringWidth(substr, _font) - _editScrollOffset; return g_gui.getStringWidth(substr, _font) - _editScrollOffset;
} }
@ -313,15 +330,16 @@ void EditableWidget::drawCaret(bool erase) {
g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height()), erase); g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height()), erase);
if (erase) { if (erase) {
Common::String character; Common::U32String character;
int width; int width;
if ((uint)_caretPos < _editString.size()) { if ((uint)_caretPos < _editString.size()) {
const byte chr = _editString[_caretPos]; Common::UnicodeBiDiText utxt(_editString);
const Common::u32char_type_t chr = utxt.visual[_caretPos];
width = g_gui.getCharWidth(chr, _font); width = g_gui.getCharWidth(chr, _font);
character = chr; character = Common::U32String(chr);
const uint last = (_caretPos > 0) ? _editString[_caretPos - 1] : 0; const uint32 last = (_caretPos > 0) ? utxt.visual[_caretPos - 1] : 0;
x += g_gui.getKerningOffset(last, chr, _font); x += g_gui.getKerningOffset(last, chr, _font);
} else { } else {
// We draw a fake space here to assure that removing the caret // We draw a fake space here to assure that removing the caret

View file

@ -94,7 +94,10 @@ protected:
void setFontStyle(ThemeEngine::FontStyle font) { _font = font; } void setFontStyle(ThemeEngine::FontStyle font) { _font = font; }
virtual bool tryInsertChar(byte c, int pos); virtual bool tryInsertChar(Common::u32char_type_t c, int pos);
int caretVisualPos(int logicalPos);
int caretLogicalPos() const;
}; };
} // End of namespace GUI } // End of namespace GUI