GUI: Limit text selection feature for RTL languages

This commit is contained in:
hax0kartik 2023-03-01 04:02:56 +05:30 committed by Eugene Sandulenko
parent 8dcf2ec58e
commit e101f5397c
2 changed files with 67 additions and 40 deletions

View file

@ -53,6 +53,7 @@ void EditableWidget::init() {
_selOffset = 0; _selOffset = 0;
_shiftPressed = _isDragging = false; _shiftPressed = _isDragging = false;
_disableSelection = g_gui.useRTL();
_align = g_gui.useRTL() ? Graphics::kTextAlignRight : Graphics::kTextAlignLeft; _align = g_gui.useRTL() ? Graphics::kTextAlignRight : Graphics::kTextAlignLeft;
_drawAlign = _align; _drawAlign = _align;
@ -128,7 +129,7 @@ void EditableWidget::handleMouseDown(int x, int y, int button, int clickCount) {
// Clear any selection // Clear any selection
if (_selOffset != 0 && !_shiftPressed) if (_selOffset != 0 && !_shiftPressed)
clearSelection(); clearSelection();
else if (_shiftPressed && _selCaretPos < 0) else if (_shiftPressed && _selCaretPos < 0 && !_disableSelection)
_selCaretPos = _caretPos; _selCaretPos = _caretPos;
if (g_gui.useRTL()) { if (g_gui.useRTL()) {
@ -149,7 +150,8 @@ void EditableWidget::handleMouseDown(int x, int y, int button, int clickCount) {
last = cur; last = cur;
} }
setCaretPos(i); setCaretPos(i);
if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos); if (_selCaretPos >= 0 && !_disableSelection)
setSelectionOffset(i - _selCaretPos);
markAsDirty(); 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) { void EditableWidget::handleMouseMoved(int x, int y, int button) {
if(_isDragging && isEnabled()) { if (_isDragging && isEnabled() && !_disableSelection) {
if(_selCaretPos < 0) if (_selCaretPos < 0)
_selCaretPos = _caretPos; _selCaretPos = _caretPos;
if (g_gui.useRTL()) { if (g_gui.useRTL()) {
@ -187,7 +189,8 @@ void EditableWidget::handleMouseMoved(int x, int y, int button) {
} }
setCaretPos(i); setCaretPos(i);
if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos); if(_selCaretPos >= 0)
setSelectionOffset(i - _selCaretPos);
markAsDirty(); markAsDirty();
} }
} }
@ -288,7 +291,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_END: case Common::KEYCODE_END:
// Move caret to end // Move caret to end
setCaretPos(caretVisualPos(_editString.size())); setCaretPos(caretVisualPos(_editString.size()));
if (state.flags & Common::KBD_SHIFT) if (state.hasFlags(Common::KBD_SHIFT))
setSelectionOffset(_editString.size() - _selCaretPos); setSelectionOffset(_editString.size() - _selCaretPos);
else else
clearSelection(); clearSelection();
@ -297,10 +300,12 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break; break;
case Common::KEYCODE_LEFT: case Common::KEYCODE_LEFT:
if(state.hasFlags(Common::KBD_SHIFT)) { if (state.hasFlags(Common::KBD_SHIFT)) {
if(_selCaretPos < 0) if (_disableSelection)
break;
if (_selCaretPos < 0)
_selCaretPos = _caretPos; _selCaretPos = _caretPos;
if(_caretPos > 0) if (_caretPos > 0)
_selOffset--; _selOffset--;
} else { } else {
clearSelection(); clearSelection();
@ -314,10 +319,12 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
break; break;
case Common::KEYCODE_RIGHT: case Common::KEYCODE_RIGHT:
if(state.hasFlags(Common::KBD_SHIFT)) { if (state.hasFlags(Common::KBD_SHIFT)) {
if(_selCaretPos < 0) if (_disableSelection)
break;
if (_selCaretPos < 0)
_selCaretPos = _caretPos; _selCaretPos = _caretPos;
if(_selOffset + _selCaretPos < (int)_editString.size()) if (_selOffset + _selCaretPos < (int)_editString.size())
_selOffset++; _selOffset++;
} else { } else {
clearSelection(); clearSelection();
@ -334,7 +341,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
case Common::KEYCODE_HOME: case Common::KEYCODE_HOME:
// Move caret to start // Move caret to start
setCaretPos(caretVisualPos(0)); setCaretPos(caretVisualPos(0));
if (state.flags & Common::KBD_SHIFT) if (state.hasFlags(Common::KBD_SHIFT))
setSelectionOffset(0 - _selCaretPos); setSelectionOffset(0 - _selCaretPos);
else else
clearSelection(); clearSelection();
@ -346,11 +353,23 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
if (state.flags & Common::KBD_CTRL) { if (state.flags & Common::KBD_CTRL) {
if (g_system->hasTextInClipboard()) { if (g_system->hasTextInClipboard()) {
Common::U32String text = g_system->getTextFromClipboard(); Common::U32String text = g_system->getTextFromClipboard();
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) { for (uint32 i = 0; i < text.size(); ++i) {
const int logicalPosition = caretLogicalPos(); const int logicalPosition = caretLogicalPos();
if (tryInsertChar(text[i], logicalPosition)) if (tryInsertChar(text[i], logicalPosition))
setCaretPos(caretVisualPos(logicalPosition + 1)); setCaretPos(caretVisualPos(logicalPosition + 1));
} }
}
dirty = true; dirty = true;
} }
} else { } else {
@ -518,10 +537,7 @@ void EditableWidget::drawCaret(bool erase) {
// EditTextWidget uses that but not ListWidget. Thus, one should check // EditTextWidget uses that but not ListWidget. Thus, one should check
// whether we can unify the drawing in the text area first to avoid // whether we can unify the drawing in the text area first to avoid
// possible glitches due to different methods used. // possible glitches due to different methods used.
if (_selOffset < 0) _inversion = (_selOffset < 0) ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
_inversion = ThemeEngine::kTextInversionFocus;
else
_inversion = ThemeEngine::kTextInversionNone;
width = MIN(editRect.width() - caretOffset, width); width = MIN(editRect.width() - caretOffset, width);
if (width > 0) { if (width > 0) {
g_gui.theme()->drawText(Common::Rect(x, y, x + width, y + editRect.height()), character, g_gui.theme()->drawText(Common::Rect(x, y, x + width, y + editRect.height()), character,

View file

@ -85,28 +85,39 @@ void EditTextWidget::drawWidget() {
selBegin = MAX(selBegin, 0); selBegin = MAX(selBegin, 0);
selEnd = MAX(selEnd, 0); selEnd = MAX(selEnd, 0);
if (!g_gui.useRTL()) {
Common::UnicodeBiDiText utxt(_editString); Common::UnicodeBiDiText utxt(_editString);
Common::U32String left = Common::U32String(utxt.visual.c_str(), utxt.visual.c_str() + selBegin); 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 selected = Common::U32String(utxt.visual.c_str() + selBegin, selEnd - selBegin);
Common::U32String right = Common::U32String(utxt.visual.c_str() + selEnd); Common::U32String right = Common::U32String(utxt.visual.c_str() + selEnd);
Common::U32StringArray parts {left, selected, right}; Common::U32StringArray parts {left, selected, right};
int scrollOffset = _editScrollOffset; int scrollOffset = _editScrollOffset;
for (int i = 0; i < parts.size(); i++) { for (uint i = 0; i < parts.size(); i++) {
if (!parts[i].size()) if (!parts[i].size())
continue; continue;
Common::U32String part = parts[i]; Common::U32String part = parts[i];
int partW = g_gui.getStringWidth(part, _font); int partW = g_gui.getStringWidth(part, _font);
int clipL = drawRect.left + (scrollOffset < 0 ? -scrollOffset : 0); 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) { if (x + partW > 0 && x < _w && clipL < drawRect.right) {
int sO = scrollOffset < 0 ? 0 : -scrollOffset; int sO = scrollOffset < 0 ? 0 : -scrollOffset;
_inversion = i == 1 ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone; _inversion = i == 1 ? ThemeEngine::kTextInversionFocus : ThemeEngine::kTextInversionNone;
g_gui.theme()->drawText(Common::Rect(clipL, y, drawRect.right, y + drawRect.height()), part, _state, g_gui.theme()->drawText(Common::Rect(clipL, y, clipR, y + drawRect.height()), part, _state,
_drawAlign, _inversion, sO, false, _font, ThemeEngine::kFontColorNormal, true, _drawAlign, _inversion, sO, false, _font, ThemeEngine::kFontColorNormal,
_textDrawableArea); true, _textDrawableArea);
} }
x += partW; x += partW;
scrollOffset -= 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);
}
} }
Common::Rect EditTextWidget::getEditRect() const { Common::Rect EditTextWidget::getEditRect() const {