[Scummvm-git-logs] scummvm master -> e101f5397c264b54b88a6fdcb3dba502cebf66eb

sev- noreply at scummvm.org
Wed Mar 1 17:42:41 UTC 2023


This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
bd52062fa7 GUI: Make text selectable
8dcf2ec58e GUI: Add selection rendering function to EditTextWidget
e101f5397c GUI: Limit text selection feature for RTL languages


Commit: bd52062fa7dd267b0a927bcf1bc6daa022bfe641
    https://github.com/scummvm/scummvm/commit/bd52062fa7dd267b0a927bcf1bc6daa022bfe641
Author: hax0kartik (agarwala.kartik at gmail.com)
Date: 2023-03-01T18:42:34+01:00

Commit Message:
GUI: Make text selectable

Changed paths:
    gui/widgets/editable.cpp
    gui/widgets/editable.h


diff --git a/gui/widgets/editable.cpp b/gui/widgets/editable.cpp
index 61b831216c1..7fa42d7eead 100644
--- a/gui/widgets/editable.cpp
+++ b/gui/widgets/editable.cpp
@@ -30,11 +30,13 @@ namespace GUI {
 
 EditableWidget::EditableWidget(GuiObject *boss, int x, int y, int w, int h, const Common::U32String &tooltip, uint32 cmd)
 	: Widget(boss, x, y, w, h, tooltip), CommandSender(boss), _cmd(cmd) {
+	setFlags(WIDGET_TRACK_MOUSE);
 	init();
 }
 
 EditableWidget::EditableWidget(GuiObject *boss, const Common::String &name, const Common::U32String &tooltip, uint32 cmd)
 	: Widget(boss, name, tooltip), CommandSender(boss), _cmd(cmd) {
+	setFlags(WIDGET_TRACK_MOUSE);
 	init();
 }
 
@@ -47,6 +49,11 @@ void EditableWidget::init() {
 
 	_editScrollOffset = 0;
 
+	_selCaretPos = -1;
+	_selOffset = 0;
+
+	_shiftPressed = _isDragging = false;
+
 	_align = g_gui.useRTL() ? Graphics::kTextAlignRight : Graphics::kTextAlignLeft;
 	_drawAlign = _align;
 
@@ -104,6 +111,92 @@ void EditableWidget::handleTickle() {
 	}
 }
 
+void EditableWidget::handleMouseDown(int x, int y, int button, int clickCount) {
+	if (!isEnabled())
+		return;
+	
+	_isDragging = true;
+	// Select all text incase of double press
+	if (clickCount > 1) {
+		_selCaretPos = 0;
+		setCaretPos(caretVisualPos(_editString.size()));
+		setSelectionOffset(_editString.size() - _selCaretPos);
+		markAsDirty();
+		return;
+	}
+	
+	// Clear any selection
+	if (_selOffset != 0 && !_shiftPressed)
+		clearSelection();
+	else if (_shiftPressed && _selCaretPos < 0)
+		_selCaretPos = _caretPos;
+
+	if (g_gui.useRTL()) {
+		x = _w - x;
+	}
+
+	x += _editScrollOffset;
+	int width = 0;
+	if (_drawAlign == Graphics::kTextAlignRight)
+		width = _editScrollOffset + getEditRect().width() - g_gui.getStringWidth(_editString, _font);
+
+	uint i, last = 0;
+	for (i = 0; i < _editString.size(); ++i) {
+		const uint cur = _editString[i];
+		width += g_gui.getCharWidth(cur, _font) + g_gui.getKerningOffset(last, cur, _font);
+		if (width >= x && width > _editScrollOffset)
+			break;
+		last = cur;
+	}
+	setCaretPos(i);
+	if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos);
+	markAsDirty();
+}
+
+void EditableWidget::handleMouseUp(int x, int y, int button, int clickCount) {
+	if(isEnabled())
+		_isDragging = false;
+}
+
+void EditableWidget::handleMouseMoved(int x, int y, int button) {
+	if(_isDragging && isEnabled()) {
+		if(_selCaretPos < 0)
+			_selCaretPos = _caretPos;
+
+		if (g_gui.useRTL()) {
+			x = _w - x;
+		}
+
+		if (x < 0 && _editScrollOffset > 0) {
+			_editScrollOffset += x;
+			if(_editScrollOffset < 0)
+				_editScrollOffset = 0;
+		}
+
+		x += _editScrollOffset;
+		int width = 0;
+		if (_drawAlign == Graphics::kTextAlignRight)
+			width = _editScrollOffset + getEditRect().width() - g_gui.getStringWidth(_editString, _font);
+		uint i, last = 0;
+		for (i = 0; i < _editString.size(); ++i) {
+			const uint cur = _editString[i];
+			width += g_gui.getCharWidth(cur, _font) + g_gui.getKerningOffset(last, cur, _font);
+			if (width >= x && width > _editScrollOffset)
+				break;
+			last = cur;
+		}
+
+		setCaretPos(i);
+		if(_selCaretPos >= 0) setSelectionOffset(i - _selCaretPos);
+		markAsDirty();
+	}
+}
+
+bool EditableWidget::handleKeyUp(Common::KeyState state) {
+	_shiftPressed = state.hasFlags(Common::KBD_SHIFT);
+	return false;
+}
+
 bool EditableWidget::handleKeyDown(Common::KeyState state) {
 	bool handled = true;
 	bool dirty = false;
@@ -117,6 +210,8 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 	if (_caretVisible)
 		drawCaret(true);
 
+	_shiftPressed = state.hasFlags(Common::KBD_SHIFT);
+
 	// Remap numeric keypad if NUM lock is *not* active.
 	// This code relies on the fact that the various KEYCODE_KP* values are
 	// consecutive.
@@ -154,12 +249,24 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 
 	case Common::KEYCODE_BACKSPACE:
 		deleteIndex = caretLogicalPos();
-		if (deleteIndex > 0) {
+		if (deleteIndex > 0 && _selOffset == 0) {
 			deleteIndex--;
 			_editString.deleteChar(deleteIndex);
 			setCaretPos(caretVisualPos(deleteIndex));
 			dirty = true;
 
+			sendCommand(_cmd, 0);
+		} else if (deleteIndex >= 0 && _selOffset != 0) {
+			int selBegin = _selCaretPos;
+			int selEnd = _selCaretPos + _selOffset;
+			if (selBegin > selEnd) 
+				SWAP(selBegin, selEnd);
+			_editString.erase(selBegin, selEnd - selBegin);
+			setCaretPos(caretVisualPos(selBegin));
+			_selCaretPos = -1;
+			_selOffset = 0;
+			dirty = true;
+
 			sendCommand(_cmd, 0);
 		}
 		forcecaret = true;
@@ -181,11 +288,23 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 	case Common::KEYCODE_END:
 		// Move caret to end
 		setCaretPos(caretVisualPos(_editString.size()));
+		if (state.flags & Common::KBD_SHIFT)
+			setSelectionOffset(_editString.size() - _selCaretPos);
+		else
+			clearSelection();
 		forcecaret = true;
 		dirty = true;
 		break;
 
 	case Common::KEYCODE_LEFT:
+		if(state.hasFlags(Common::KBD_SHIFT)) {
+			if(_selCaretPos < 0)
+				_selCaretPos = _caretPos;
+			if(_caretPos > 0)
+			_selOffset--;
+		} else {
+			clearSelection();
+		}
 		// Move caret one left (if possible)
 		if (_caretPos > 0) {
 			dirty = setCaretPos(_caretPos - 1);
@@ -195,6 +314,14 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 		break;
 
 	case Common::KEYCODE_RIGHT:
+		if(state.hasFlags(Common::KBD_SHIFT)) {
+			if(_selCaretPos < 0)
+				_selCaretPos = _caretPos;
+			if(_selOffset + _selCaretPos < (int)_editString.size())
+			_selOffset++;
+		} else {
+			clearSelection();
+		}
 		// Move caret one right (if possible)
 		if (_caretPos < (int)_editString.size()) {
 			dirty = setCaretPos(_caretPos + 1);
@@ -207,6 +334,10 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 	case Common::KEYCODE_HOME:
 		// Move caret to start
 		setCaretPos(caretVisualPos(0));
+		if (state.flags & Common::KBD_SHIFT)
+			setSelectionOffset(0 - _selCaretPos);
+		else
+			clearSelection();
 		forcecaret = true;
 		dirty = true;
 		break;
@@ -229,8 +360,14 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 
 	case Common::KEYCODE_c:
 		if (state.flags & Common::KBD_CTRL) {
-			if (!getEditString().empty())
-				g_system->setTextInClipboard(getEditString());
+			if (!getEditString().empty()) {
+				int selBegin = _selCaretPos;
+				int selEnd = _selCaretPos + _selOffset;
+				if (selBegin > selEnd)
+					SWAP(selBegin, selEnd);
+				const Common::U32String selected(getEditString().begin() + selBegin, getEditString().begin() + selEnd);
+				g_system->setTextInClipboard(selected);
+			}
 		} else {
 			defaultKeyDownHandler(state, dirty, forcecaret, handled);
 		}
@@ -262,6 +399,7 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 				dirty = setCaretPos(_editString.size());
 				forcecaret = true;
 			}
+			clearSelection();
 			break;
 		}
 #endif
@@ -280,12 +418,27 @@ bool EditableWidget::handleKeyDown(Common::KeyState state) {
 }
 
 void EditableWidget::defaultKeyDownHandler(Common::KeyState &state, bool &dirty, bool &forcecaret, bool &handled) {
-	const int logicalPosition = caretLogicalPos();
-	if (tryInsertChar(state.ascii, logicalPosition)) {
-		setCaretPos(caretVisualPos(logicalPosition + 1));
+	if (isCharAllowed(state.ascii)) {
+		// Incase of a selection, replace the selection with the character
+		if (_selCaretPos >= 0) {
+			int selBegin = _selCaretPos;
+			int selEnd = _selCaretPos + _selOffset;
+			if (selBegin > selEnd) 
+				SWAP(selBegin, selEnd);
+			_editString.replace(selBegin, selEnd - selBegin, Common::U32String(state.ascii));
+			if(_editString.size() > 0)
+				selBegin++;
+			setCaretPos(caretVisualPos(selBegin));
+			_selCaretPos = -1;
+			_selOffset = 0;
+		} else {
+			// Insert char normally at caretPos
+			const int logicalPosition = caretLogicalPos();
+			_editString.insertChar(state.ascii, logicalPosition);
+			setCaretPos(caretVisualPos(logicalPosition + 1));
+		}
 		dirty = true;
 		forcecaret = true;
-
 		sendCommand(_cmd, 0);
 	} else {
 		handled = false;
@@ -298,6 +451,12 @@ int EditableWidget::getCaretOffset() const {
 	return g_gui.getStringWidth(substr, _font) - _editScrollOffset;
 }
 
+int EditableWidget::getSelectionCarretOffset() const {
+	Common::UnicodeBiDiText utxt(_editString);
+	Common::U32String substr(utxt.visual.begin(), utxt.visual.begin() + _selCaretPos);
+	return g_gui.getStringWidth(substr, _font) - _editScrollOffset;
+}
+
 void EditableWidget::drawCaret(bool erase) {
 	// Only draw if item is visible
 	if (!isVisible() || !_boss->isVisible())
@@ -359,6 +518,10 @@ 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;
 		width = MIN(editRect.width() - caretOffset, width);
 		if (width > 0) {
 			g_gui.theme()->drawText(Common::Rect(x, y, x + width, y + editRect.height()), character,
@@ -410,4 +573,14 @@ void EditableWidget::makeCaretVisible() {
 	drawCaret(false);
 }
 
+void EditableWidget::clearSelection() {
+	_selCaretPos = -1;
+	_selOffset = 0;
+	markAsDirty();
+}
+
+void EditableWidget::setSelectionOffset(int newOffset) {
+	_selOffset = newOffset;
+}
+
 } // End of namespace GUI
diff --git a/gui/widgets/editable.h b/gui/widgets/editable.h
index bdc9213d553..8c43347b66e 100644
--- a/gui/widgets/editable.h
+++ b/gui/widgets/editable.h
@@ -52,6 +52,12 @@ protected:
 
 	int			_editScrollOffset;
 
+	int			_selCaretPos;
+	int			_selOffset;
+	bool		_shiftPressed;
+	bool		_isDragging;
+	bool		_disableSelection;
+
 	Graphics::TextAlign _align;
 	Graphics::TextAlign _drawAlign;
 
@@ -70,10 +76,15 @@ public:
 	virtual const Common::U32String &getEditString() const	{ return _editString; }
 
 	void handleTickle() override;
+	void handleMouseDown(int x, int y, int button, int clickCount) override;
+	void handleMouseUp(int x, int y, int button, int clickCount) override;
+	void handleMouseMoved(int x, int y, int button) override;
 	bool handleKeyDown(Common::KeyState state) override;
+	bool handleKeyUp(Common::KeyState state) override;
 	void reflowLayout() override;
 
 	bool setCaretPos(int newPos);
+	void setSelectionOffset(int newOffset);
 
 protected:
 	virtual void startEditMode() = 0;
@@ -86,6 +97,7 @@ protected:
 	 */
 	virtual Common::Rect getEditRect() const = 0;
 	virtual int getCaretOffset() const;
+	virtual int getSelectionCarretOffset() const;
 	void drawCaret(bool erase);
 	bool adjustOffset();
 	void makeCaretVisible();
@@ -99,6 +111,8 @@ protected:
 
 	int caretVisualPos(int logicalPos);
 	int caretLogicalPos() const;
+
+	void clearSelection();
 };
 
 } // End of namespace GUI


Commit: 8dcf2ec58eaff516e396cd96cb5688c49f7607cf
    https://github.com/scummvm/scummvm/commit/8dcf2ec58eaff516e396cd96cb5688c49f7607cf
Author: hax0kartik (agarwala.kartik at gmail.com)
Date: 2023-03-01T18:42:34+01:00

Commit Message:
GUI: Add selection rendering function to EditTextWidget

This code has been written by @sev-
TODOs:
    1. Investigate and fix characters jumping/shaking during selection.
    2. Make code compatible with RTL languages such as hebrew.

Changed paths:
    gui/widgets/edittext.cpp
    gui/widgets/edittext.h


diff --git a/gui/widgets/edittext.cpp b/gui/widgets/edittext.cpp
index 9ed0f984367..1df81800d4b 100644
--- a/gui/widgets/edittext.cpp
+++ b/gui/widgets/edittext.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "common/system.h"
+#include "common/unicode-bidi.h"
 #include "gui/widgets/edittext.h"
 #include "gui/gui-manager.h"
 
@@ -46,6 +47,7 @@ EditTextWidget::EditTextWidget(GuiObject *boss, const Common::String &name, cons
 	_finishCmd = finishCmd;
 
 	_leftPadding = _rightPadding = 0;
+	_shiftPressed = _isDragging = false;
 
 	setEditString(text);
 	setFontStyle(font);
@@ -63,38 +65,6 @@ void EditTextWidget::reflowLayout() {
 	EditableWidget::reflowLayout();
 }
 
-void EditTextWidget::handleMouseDown(int x, int y, int button, int clickCount) {
-	if (!isEnabled())
-		return;
-
-	// First remove caret
-	if (_caretVisible)
-		drawCaret(true);
-
-	if (g_gui.useRTL()) {
-		x = _w - x;
-	}
-
-	x += _editScrollOffset;
-	int width = 0;
-	if (_drawAlign == Graphics::kTextAlignRight)
-		width = _editScrollOffset + getEditRect().width() - g_gui.getStringWidth(_editString, _font);
-
-	uint i;
-
-	uint last = 0;
-	for (i = 0; i < _editString.size(); ++i) {
-		const uint cur = _editString[i];
-		width += g_gui.getCharWidth(cur, _font) + g_gui.getKerningOffset(last, cur, _font);
-		if (width >= x && width > _editScrollOffset + _leftPadding)
-			break;
-		last = cur;
-	}
-
-	setCaretPos(i);
-	markAsDirty();
-}
-
 void EditTextWidget::drawWidget() {
 	g_gui.theme()->drawWidgetBackground(Common::Rect(_x, _y, _x + _w, _y + _h),
 	                                    ThemeEngine::kWidgetBackgroundEditText);
@@ -105,10 +75,38 @@ void EditTextWidget::drawWidget() {
 	drawRect.translate(_x, _y);
 	setTextDrawableArea(drawRect);
 
-	g_gui.theme()->drawText(
-			drawRect,
-			_editString, _state, _drawAlign, ThemeEngine::kTextInversionNone,
-			-_editScrollOffset, false, _font, ThemeEngine::kFontColorNormal, true, _textDrawableArea);
+	int x = -_editScrollOffset;
+	int y = drawRect.top;
+
+	int selBegin = _selCaretPos;
+	int selEnd = _selOffset + _selCaretPos;
+	if (selBegin > selEnd)
+		SWAP(selBegin, selEnd);
+	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);
+		}
+		x += partW;
+		scrollOffset -= partW;
+	}
 }
 
 Common::Rect EditTextWidget::getEditRect() const {
@@ -133,9 +131,10 @@ void EditTextWidget::receivedFocusWidget() {
 }
 
 void EditTextWidget::lostFocusWidget() {
-	// If we lose focus, 'commit' the user changes
+	// If we lose focus, 'commit' the user changes and clear selection
 	_backupString = _editString;
 	drawCaret(true);
+	clearSelection();
 
 	g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
 }
diff --git a/gui/widgets/edittext.h b/gui/widgets/edittext.h
index 7177c9278cd..9b8fa682e2b 100644
--- a/gui/widgets/edittext.h
+++ b/gui/widgets/edittext.h
@@ -42,8 +42,6 @@ public:
 
 	void setEditString(const Common::U32String &str) override;
 
-	void handleMouseDown(int x, int y, int button, int clickCount) override;
-
 	bool wantsFocus() override { return true; }
 
 	void reflowLayout() override;


Commit: e101f5397c264b54b88a6fdcb3dba502cebf66eb
    https://github.com/scummvm/scummvm/commit/e101f5397c264b54b88a6fdcb3dba502cebf66eb
Author: hax0kartik (agarwala.kartik at gmail.com)
Date: 2023-03-01T18:42:34+01:00

Commit Message:
GUI: Limit text selection feature for RTL languages

Changed paths:
    gui/widgets/editable.cpp
    gui/widgets/edittext.cpp


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




More information about the Scummvm-git-logs mailing list