From e101f5397c264b54b88a6fdcb3dba502cebf66eb Mon Sep 17 00:00:00 2001 From: hax0kartik Date: Wed, 1 Mar 2023 04:02:56 +0530 Subject: [PATCH] GUI: Limit text selection feature for RTL languages --- gui/widgets/editable.cpp | 56 ++++++++++++++++++++++++++-------------- gui/widgets/edittext.cpp | 51 ++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 40 deletions(-) diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp index 7fa42d7eead..d968f458f0a 100644 --- a/gui/widgets/editable.cpp +++ b/gui/widgets/editable.cpp @@ -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; @@ -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()) { @@ -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(); } @@ -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()) { @@ -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(); } } @@ -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(); @@ -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(); } @@ -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(); } @@ -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(); @@ -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; } @@ -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, diff --git a/gui/widgets/edittext.cpp b/gui/widgets/edittext.cpp index 1df81800d4b..2e17d7b31ad 100644 --- a/gui/widgets/edittext.cpp +++ b/gui/widgets/edittext.cpp @@ -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); } }