Skip to content

Commit

Permalink
GUI: Limit text selection feature for RTL languages
Browse files Browse the repository at this point in the history
  • Loading branch information
hax0kartik committed Mar 1, 2023
1 parent d76f6ca commit d145b6d
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 40 deletions.
56 changes: 36 additions & 20 deletions gui/widgets/editable.cpp
Expand Up @@ -53,6 +53,7 @@ void EditableWidget::init() {
_selOffset = 0;

_shiftPressed = _isDragging = false;
_disableSelection = g_gui.useRTL();

_align = g_gui.useRTL() ? Graphics::kTextAlignRight : Graphics::kTextAlignLeft;
_drawAlign = _align;
Expand Down Expand Up @@ -128,7 +129,7 @@ void EditableWidget::handleMouseDown(int x, int y, int button, int clickCount) {
// Clear any selection
if (_selOffset != 0 && !_shiftPressed)
clearSelection();
else if (_shiftPressed && _selCaretPos < 0)
else if (_shiftPressed && _selCaretPos < 0 && !_disableSelection)
_selCaretPos = _caretPos;

if (g_gui.useRTL()) {
Expand All @@ -149,7 +150,8 @@ void EditableWidget::handleMouseDown(int x, int y, int button, int clickCount) {
last = cur;
}
setCaretPos(i);
if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos);
if (_selCaretPos >= 0 && !_disableSelection)
setSelectionOffset(i - _selCaretPos);
markAsDirty();
}

Expand All @@ -159,8 +161,8 @@ void EditableWidget::handleMouseUp(int x, int y, int button, int clickCount) {
}

void EditableWidget::handleMouseMoved(int x, int y, int button) {
if(_isDragging && isEnabled()) {
if(_selCaretPos < 0)
if (_isDragging && isEnabled() && !_disableSelection) {
if (_selCaretPos < 0)
_selCaretPos = _caretPos;

if (g_gui.useRTL()) {
Expand All @@ -187,7 +189,8 @@ void EditableWidget::handleMouseMoved(int x, int y, int button) {
}

setCaretPos(i);
if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos);
if(_selCaretPos >= 0)
setSelectionOffset(i - _selCaretPos);
markAsDirty();
}
}
Expand Down Expand Up @@ -288,7 +291,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_END:
// Move caret to end
setCaretPos(caretVisualPos(_editString.size()));
if (state.flags & Common::KBD_SHIFT)
if (state.hasFlags(Common::KBD_SHIFT))
setSelectionOffset(_editString.size() - _selCaretPos);
else
clearSelection();
Expand All @@ -297,11 +300,13 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break;

case Common::KEYCODE_LEFT:
if(state.hasFlags(Common::KBD_SHIFT)) {
if(_selCaretPos < 0)
if (state.hasFlags(Common::KBD_SHIFT)) {
if (_disableSelection)
break;
if (_selCaretPos < 0)
_selCaretPos = _caretPos;
if(_caretPos > 0)
_selOffset--;
if (_caretPos > 0)
_selOffset--;
} else {
clearSelection();
}
Expand All @@ -314,11 +319,13 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break;

case Common::KEYCODE_RIGHT:
if(state.hasFlags(Common::KBD_SHIFT)) {
if(_selCaretPos < 0)
if (state.hasFlags(Common::KBD_SHIFT)) {
if (_disableSelection)
break;
if (_selCaretPos < 0)
_selCaretPos = _caretPos;
if(_selOffset + _selCaretPos < (int)_editString.size())
_selOffset++;
if (_selOffset + _selCaretPos < (int)_editString.size())
_selOffset++;
} else {
clearSelection();
}
Expand All @@ -334,7 +341,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_HOME:
// Move caret to start
setCaretPos(caretVisualPos(0));
if (state.flags & Common::KBD_SHIFT)
if (state.hasFlags(Common::KBD_SHIFT))
setSelectionOffset(0 - _selCaretPos);
else
clearSelection();
Expand All @@ -346,10 +353,22 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
if (state.flags & Common::KBD_CTRL) {
if (g_system->hasTextInClipboard()) {
Common::U32String text = g_system->getTextFromClipboard();
for (uint32 i = 0; i < text.size(); ++i) {
if (_selOffset != 0) {
int selBegin = _selCaretPos;
int selEnd = _selCaretPos + _selOffset;
if (selBegin > selEnd)
SWAP(selBegin, selEnd);
_editString.replace(selBegin, selEnd - selBegin, text);
setCaretPos(caretVisualPos(selBegin));
const int logicalPosition = caretLogicalPos();
setCaretPos(caretVisualPos(logicalPosition + text.size()));
clearSelection();
} else {
for (uint32 i = 0; i < text.size(); ++i) {
const int logicalPosition = caretLogicalPos();
if (tryInsertChar(text[i], logicalPosition))
setCaretPos(caretVisualPos(logicalPosition + 1));
}
}
dirty = true;
}
Expand Down Expand Up @@ -518,10 +537,7 @@ void EditableWidget::drawCaret(bool erase) {
// EditTextWidget uses that but not ListWidget. Thus, one should check
// whether we can unify the drawing in the text area first to avoid
// possible glitches due to different methods used.
if (_selOffset < 0)
_inversion = ThemeEngine::kTextInversionFocus;
else
_inversion = ThemeEngine::kTextInversionNone;
_inversion = (_selOffset < 0) ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
width = MIN(editRect.width() - caretOffset, width);
if (width > 0) {
g_gui.theme()->drawText(Common::Rect(x, y, x + width, y + editRect.height()), character,
Expand Down
51 changes: 31 additions & 20 deletions gui/widgets/edittext.cpp
Expand Up @@ -85,27 +85,38 @@ void EditTextWidget::drawWidget() {
selBegin = MAX(selBegin, 0);
selEnd = MAX(selEnd, 0);

Common::UnicodeBiDiText utxt(_editString);
Common::U32String left = Common::U32String(utxt.visual.c_str(), utxt.visual.c_str() + selBegin);
Common::U32String selected = Common::U32String(utxt.visual.c_str() + selBegin, selEnd - selBegin);
Common::U32String right = Common::U32String(utxt.visual.c_str() + selEnd);
Common::U32StringArray parts {left, selected, right};
int scrollOffset = _editScrollOffset;
for (int i = 0; i < parts.size(); i++) {
if (!parts[i].size())
continue;
Common::U32String part = parts[i];
int partW = g_gui.getStringWidth(part, _font);
int clipL = drawRect.left + (scrollOffset < 0 ? -scrollOffset : 0);
if (x + partW > 0 && x < _w && clipL < drawRect.right) {
int sO = scrollOffset < 0 ? 0 : -scrollOffset;
_inversion = i == 1 ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
g_gui.theme()->drawText(Common::Rect(clipL, y, drawRect.right, y + drawRect.height()), part, _state,
_drawAlign, _inversion, sO, false, _font, ThemeEngine::kFontColorNormal, true,
_textDrawableArea);
if (!g_gui.useRTL()) {
Common::UnicodeBiDiText utxt(_editString);
Common::U32String left = Common::U32String(utxt.visual.c_str(), utxt.visual.c_str() + selBegin);
Common::U32String selected = Common::U32String(utxt.visual.c_str() + selBegin, selEnd - selBegin);
Common::U32String right = Common::U32String(utxt.visual.c_str() + selEnd);
Common::U32StringArray parts {left, selected, right};
int scrollOffset = _editScrollOffset;
for (uint i = 0; i < parts.size(); i++) {
if (!parts[i].size())
continue;
Common::U32String part = parts[i];
int partW = g_gui.getStringWidth(part, _font);
int clipL = drawRect.left + (scrollOffset < 0 ? -scrollOffset : 0);
int clipR = MIN(clipL + partW, (int)drawRect.right);
if (x + partW > 0 && x < _w && clipL < drawRect.right) {
int sO = scrollOffset < 0 ? 0 : -scrollOffset;
_inversion = i == 1 ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
g_gui.theme()->drawText(Common::Rect(clipL, y, clipR, y + drawRect.height()), part, _state,
_drawAlign, _inversion, sO, false, _font, ThemeEngine::kFontColorNormal,
true, _textDrawableArea);
}
x += partW;
scrollOffset -= partW;
}
x += partW;
scrollOffset -= partW;
} else {
// The above method does not render RTL languages correctly, so fallback to default method
// There are only two possible cases, either the whole string has been selected
// or nothing has been selected.
_inversion = _selOffset ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
g_gui.theme()->drawText(drawRect, _editString, _state, _drawAlign, _inversion,
-_editScrollOffset, false, _font, ThemeEngine::kFontColorNormal, true,
_textDrawableArea);
}
}

Expand Down

0 comments on commit d145b6d

Please sign in to comment.