summaryrefslogtreecommitdiffstats
path: root/src/gui/text/qtextlayout.cpp
diff options
context:
space:
mode:
authorVolker Hilsheimer <volker.hilsheimer@qt.io>2021-10-15 10:47:57 +0200
committerVolker Hilsheimer <volker.hilsheimer@qt.io>2021-10-18 13:05:42 +0200
commitf54044d4a92ab5ef04c11bc3ca9f064e91d97e63 (patch)
tree9a92482fb7296730793f904fa047d88c2841fed4 /src/gui/text/qtextlayout.cpp
parente652fa4d3e0f444f7a42d6a00cc39cf3ab79a6ec (diff)
Fix cursor placement at left and right ends of bidi text
In a text line that has a change of direction at either end of the text, the cursor needs to be positioned where the next character is inserted, or where backspace deletes the previous character. In bidi text, this is ambiguous as illustrated by this example: abcشزذ Depending on whether this string was typed in a left-to-right document or in a right-to-left document, it could be first latin, then arabic; or it could be first arabic, then latin. If a general left-to-right context, cursor position 0 should be in front of the 'a', and cursor position 6 should be at the end of the arabic text, in the visual middle of the line. Cursor position 3 can be either after the 'c' if the next character typed would be latin, or at the visual end of the line if the next character will be arabic. Qt calculated the cursor position past the right end of the text as 3 (which is not wrong, but 3 has two visual positions), and placed the cursor at the visual end of the line (favoring the right-to-left alternative). Backspace would then delete the 'c', writing a new latin character would insert a 'd' next to the 'c', writing a new arabic character would insert it also in the middle - none of these operations happen at the visual end of the line, where the cursor was blinking. To fix this, we take into account the general layout of the text, which is typically based on the document, or the user's locale setting and UI translation, and calculate the cursor position accordingly: if we are past the visual end of the document on either side, then the cursor position is either 0 or the last character of the text, depending on the direction of the QTextEngine used. This way, the cursor ends up in the middle of the document when we click beyond the end of the line, which is where characters are removed and inserted. Typing a 'd' at this point will make the cursor jump to the end, where the d is added. There are still corner cases: clicking on the right-most arabic character calculates the cursor position as 3, which is then ambiguous, as it can be either at the visual end of the string, or next to the 'c'. َQt makes the inconsistent choice to place the cursor at the visual end, showing the left-to-right indicator, but pressing a 'd' adds the 'd' after the 'c' in the middle of the text. Fixes: QTBUG-88529 Pick-to: 6.2 Change-Id: Idccd4c4deead2bce0e858189f9aef414857eb8af Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Diffstat (limited to 'src/gui/text/qtextlayout.cpp')
-rw-r--r--src/gui/text/qtextlayout.cpp35
1 files changed, 9 insertions, 26 deletions
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index 5b9c10ee52..845642bb6e 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -2909,18 +2909,10 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
bool visual = eng->visualCursorMovement();
if (x <= 0) {
// left of first item
- int item = visualOrder[0]+firstItem;
- QScriptItem &si = eng->layoutData->items[item];
- if (!si.num_glyphs)
- eng->shape(item);
- int pos = si.position;
- if (si.analysis.bidiLevel % 2)
- pos += eng->length(item);
- pos = qMax(line.from, pos);
- pos = qMin(line.from + line_length, pos);
- return pos;
- } else if (x < line.textWidth
- || (line.justified && x < line.width)) {
+ if (eng->isRightToLeft())
+ return line.from + line_length;
+ return line.from;
+ } else if (x < line.textWidth || (line.justified && x < line.width)) {
// has to be in one of the runs
QFixed pos;
bool rtl = eng->isRightToLeft();
@@ -3070,26 +3062,17 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
}
}
// right of last item
-// qDebug("right of last");
- int item = visualOrder[nItems-1]+firstItem;
- QScriptItem &si = eng->layoutData->items[item];
- if (!si.num_glyphs)
- eng->shape(item);
- int pos = si.position;
- if (!(si.analysis.bidiLevel % 2))
- pos += eng->length(item);
- pos = qMax(line.from, pos);
-
- int maxPos = line.from + line_length;
+ int pos = line.from;
+ if (!eng->isRightToLeft())
+ pos += line_length;
// except for the last line we assume that the
// character between lines is a space and we want
// to position the cursor to the left of that
// character.
- if (this->index < eng->lines.count() - 1)
- maxPos = eng->previousLogicalPosition(maxPos);
+ if (index < eng->lines.count() - 1)
+ pos = qMin(eng->previousLogicalPosition(pos), pos);
- pos = qMin(pos, maxPos);
return pos;
}