Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GUI: Support unicode and BiDi in editable widget #4144

Merged
merged 1 commit into from Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions gui/ThemeEngine.cpp
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions gui/ThemeEngine.h
Expand Up @@ -432,9 +432,9 @@ class ThemeEngine {

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;

//@}

Expand Down
2 changes: 1 addition & 1 deletion gui/editgamedialog.cpp
Expand Up @@ -92,7 +92,7 @@ class DomainEditTextWidget : public EditTextWidget {
: 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;
Expand Down
4 changes: 2 additions & 2 deletions gui/gui-manager.h
Expand Up @@ -115,8 +115,8 @@ class GuiManager : public Common::Singleton<GuiManager>, public CommandSender {
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.
Expand Down
52 changes: 35 additions & 17 deletions gui/widgets/editable.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -75,14 +76,22 @@ 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;
}
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()) {
Expand All @@ -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;
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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;
Expand All @@ -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;
Expand All @@ -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;
}
Expand Down Expand Up @@ -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;

Expand All @@ -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;
}

Expand Down Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion gui/widgets/editable.h
Expand Up @@ -94,7 +94,10 @@ class EditableWidget : public Widget, public CommandSender {

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
Expand Down