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;
}
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;
}
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;
}

View File

@ -432,9 +432,9 @@ public:
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) {}
protected:
bool tryInsertChar(byte c, int pos) override {
bool tryInsertChar(Common::u32char_type_t c, int pos) override {
if (Common::isAlnum(c) || c == '-' || c == '_') {
_editString.insertChar(c, pos);
return true;

View File

@ -115,8 +115,8 @@ public:
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::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 getKerningOffset(byte left, byte right, ThemeEngine::FontStyle font = ThemeEngine::kFontStyleBold) const { return _theme->getKerningOffset(left, right, font); }
int getCharWidth(uint32 c, ThemeEngine::FontStyle style = ThemeEngine::kFontStyleBold) const { return _theme->getCharWidth(c, style); }
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.

View File

@ -21,6 +21,7 @@
#include "common/rect.h"
#include "common/system.h"
#include "common/unicode-bidi.h"
#include "gui/widgets/editable.h"
#include "gui/gui-manager.h"
#include "graphics/font.h"
@ -75,7 +76,7 @@ void EditableWidget::setEditString(const Common::U32String &str) {
_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) {
_editString.insertChar(c, pos);
return true;
@ -83,6 +84,14 @@ bool EditableWidget::tryInsertChar(byte c, int pos) {
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() {
uint32 time = g_system->getMillis();
if (_caretTime < time && isEnabled()) {
@ -95,6 +104,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
bool handled = true;
bool dirty = false;
bool forcecaret = false;
int deleteIndex;
if (!isEnabled())
return false;
@ -139,9 +149,11 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break;
case Common::KEYCODE_BACKSPACE:
if (_caretPos > 0) {
_caretPos--;
_editString.deleteChar(_caretPos);
deleteIndex = caretLogicalPos();
if (deleteIndex > 0) {
deleteIndex--;
_editString.deleteChar(deleteIndex);
setCaretPos(caretVisualPos(deleteIndex));
dirty = true;
sendCommand(_cmd, 0);
@ -150,8 +162,10 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break;
case Common::KEYCODE_DELETE:
if (_caretPos < (int)_editString.size()) {
_editString.deleteChar(_caretPos);
deleteIndex = caretLogicalPos();
if (deleteIndex < (int)_editString.size()) {
_editString.deleteChar(deleteIndex);
setCaretPos(caretVisualPos(deleteIndex));
dirty = true;
sendCommand(_cmd, 0);
@ -162,7 +176,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_DOWN:
case Common::KEYCODE_END:
// Move caret to end
setCaretPos(_editString.size());
setCaretPos(caretVisualPos(_editString.size()));
forcecaret = true;
dirty = true;
break;
@ -188,7 +202,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_UP:
case Common::KEYCODE_HOME:
// Move caret to start
setCaretPos(0);
setCaretPos(caretVisualPos(0));
forcecaret = true;
dirty = true;
break;
@ -198,8 +212,9 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
if (g_system->hasTextInClipboard()) {
Common::U32String text = g_system->getTextFromClipboard();
for (uint32 i = 0; i < text.size(); ++i) {
if (tryInsertChar(text[i], _caretPos))
++_caretPos;
const int logicalPosition = caretLogicalPos();
if (tryInsertChar(text[i], logicalPosition))
setCaretPos(caretVisualPos(logicalPosition + 1));
}
dirty = true;
}
@ -261,8 +276,9 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
}
void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) {
if (state.ascii < 256 && tryInsertChar((byte)state.ascii, _caretPos)) {
_caretPos++;
const int logicalPosition = caretLogicalPos();
if (tryInsertChar(state.ascii, logicalPosition)) {
setCaretPos(caretVisualPos(logicalPosition + 1));
dirty = true;
forcecaret = true;
@ -273,7 +289,8 @@ void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty,
}
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;
}
@ -313,15 +330,16 @@ void EditableWidget::drawCaret(bool erase) {
g_gui.theme()->drawCaret(Common::Rect(x, y, x + 1, y + editRect.height()), erase);
if (erase) {
Common::String character;
Common::U32String character;
int width;
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);
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);
} else {
// 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; }
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