drop BiDi support

still wonky on selection:
https://ibb.co/D4bWVqX

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
This commit is contained in:
Ivailo Monev 2022-01-02 16:46:26 +02:00
parent 57dbb8d1cd
commit a699a16de9
7 changed files with 54 additions and 389 deletions

View file

@ -4789,17 +4789,11 @@ void QPainter::drawText(const QPointF &p, const QString &str)
engine.shapeLine(line);
int nItems = engine.layoutData->items.size();
QVarLengthArray<int> visualOrder(nItems);
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = engine.layoutData->items[i].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
QFixed x = QFixed::fromReal(p.x());
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i];
const QScriptItem &si = engine.layoutData->items.at(item);
const QScriptItem &si = engine.layoutData->items.at(i);
if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
x += si.width;
continue;
@ -4808,7 +4802,7 @@ void QPainter::drawText(const QPointF &p, const QString &str)
QTextItemInt gf(si, &f);
gf.glyphs = engine.shapedGlyphs(&si);
gf.chars = engine.layoutData->string.unicode() + si.position;
gf.num_chars = engine.length(item);
gf.num_chars = engine.length(i);
if (engine.forceJustification) {
for (int j=0; j<gf.glyphs.numGlyphs; ++j)
gf.width += gf.glyphs.effectiveAdvance(j);
@ -6379,9 +6373,7 @@ start_lengthVariant:
qreal advance = line.horizontalAdvance();
xoff = 0;
if (tf & Qt::AlignRight) {
QTextEngine *eng = textLayout.engine();
xoff = r.width() - advance -
eng->leadingSpaceWidth(eng->lines[line.lineNumber()]).toReal();
xoff = r.width() - advance;
}
else if (tf & Qt::AlignHCenter)
xoff = (r.width() - advance) / 2;

View file

@ -1104,24 +1104,14 @@ void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &
qreal x(point.x());
qreal y(point.y());
QVarLengthArray<int> visualOrder(nItems);
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i];
QScriptItem &si = eng->layoutData->items[item];
QScriptItem &si = eng->layoutData->items[i];
if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
QGlyphLayout glyphs = eng->shapedGlyphs(&si);
QFontEngine *fe = f.d->engineForScript(si.analysis.script);
Q_ASSERT(fe);
fe->addOutlineToPath(x, y, glyphs, this,
si.analysis.bidiLevel % 2
? QTextItem::RenderFlags(QTextItem::RightToLeft)
: QTextItem::RenderFlags(0));
fe->addOutlineToPath(x, y, glyphs, this, QTextItem::RenderFlags(0));
const qreal lw = fe->lineThickness().toReal();
if (f.d->underline) {

View file

@ -113,8 +113,6 @@ static bool qHB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
QVarLengthGlyphLayoutArray qglyphs(shaper_item->num_glyphs);
QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
if (shaper_item->item.bidiLevel % 2)
shaperFlags |= QTextEngine::RightToLeft;
int nGlyphs = shaper_item->num_glyphs;
bool result = fe->stringToCMap(reinterpret_cast<const QChar *>(shaper_item->string + shaper_item->item.pos),
@ -270,8 +268,6 @@ static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
//qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
// qDebug("offset = %f", offsetBase);
bool rightToLeft = item->item.bidiLevel % 2;
int i;
unsigned char lastCmb = 0;
HB_GlyphMetrics attachmentRect;
@ -365,13 +361,8 @@ static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
attachmentRect = unitedAttachmentRect;
lastCmb = cmb;
if (rightToLeft) {
item->offsets[gfrom+i].x = p.x;
item->offsets[gfrom+i].y = p.y;
} else {
item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
}
item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
item->advances[gfrom+i] = 0;
}
}

View file

@ -400,7 +400,6 @@ typedef struct {
uint32_t pos;
uint32_t length;
HB_Script script;
uint8_t bidiLevel;
} HB_ScriptItem;
typedef struct {

View file

@ -78,8 +78,7 @@ public:
// the font and because Japanese and Chinese are also aliases of the script "Common",
// doing this would break too many things. So instead we only pass the full stop
// along, and nothing else.
if (m_analysis[i].bidiLevel == m_analysis[start].bidiLevel
&& m_analysis[i].flags == m_analysis[start].flags
if (m_analysis[i].flags == m_analysis[start].flags
&& (m_analysis[i].script == m_analysis[start].script || m_string[i] == QLatin1Char('.'))
&& m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject
&& i - start < MaxItemLength)
@ -99,88 +98,12 @@ private:
};
}
// ----------------------------------------------------------------------------
//
// The BiDi algorithm
//
// ----------------------------------------------------------------------------
#if (BIDI_DEBUG >= 1)
static const char *directions[] = {
"DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
"DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN", "DirLRI",
"DirRLI", "DirFSI", "DirPDI"
};
#endif
void QTextEngine::bidiReorder(int numItems, const quint8 *levels, int *visualOrder)
{
// first find highest and lowest levels
quint8 levelLow = 128;
quint8 levelHigh = 0;
int i = 0;
while (i < numItems) {
//printf("level = %d\n", r->level);
if (levels[i] > levelHigh)
levelHigh = levels[i];
if (levels[i] < levelLow)
levelLow = levels[i];
i++;
}
// implements reordering of the line (L2 according to BiDi spec):
// L2. From the highest level found in the text to the lowest odd level on each line,
// reverse any contiguous sequence of characters that are at that level or higher.
// reversing is only done up to the lowest odd level
if(!(levelLow%2)) levelLow++;
#if (BIDI_DEBUG >= 1)
// qDebug() << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh;
#endif
int count = numItems - 1;
for (i = 0; i < numItems; i++)
visualOrder[i] = i;
while(levelHigh >= levelLow) {
int i = 0;
while (i < count) {
while(i < count && levels[i] < levelHigh) i++;
int start = i;
while(i <= count && levels[i] >= levelHigh) i++;
int end = i-1;
if(start != end) {
//qDebug() << "reversing from " << start << " to " << end;
for(int j = 0; j < (end-start+1)/2; j++) {
int tmp = visualOrder[start+j];
visualOrder[start+j] = visualOrder[end-j];
visualOrder[end-j] = tmp;
}
}
i++;
}
levelHigh--;
}
#if (BIDI_DEBUG >= 1)
// qDebug() << "visual order is:";
// for (i = 0; i < numItems; i++)
// qDebug() << visualOrder[i];
#endif
}
// ask the font engine to find out which glyphs (as an index in the specific font) to use for the text in one item.
static bool stringToGlyphs(HB_ShaperItem *item, QGlyphLayout *glyphs, QFontEngine *fontEngine)
{
int nGlyphs = item->num_glyphs;
QTextEngine::ShaperFlags shaperFlags(QTextEngine::GlyphIndicesOnly);
if (item->item.bidiLevel % 2)
shaperFlags |= QTextEngine::RightToLeft;
bool result = fontEngine->stringToCMap(reinterpret_cast<const QChar *>(item->string + item->item.pos), item->item.length, glyphs, &nGlyphs, shaperFlags);
item->num_glyphs = nGlyphs;
@ -298,7 +221,6 @@ void QTextEngine::shapeTextWithHarfbuzz(int item) const
entire_shaper_item.item.script = (HB_Script)si.analysis.script;
entire_shaper_item.item.pos = si.position;
entire_shaper_item.item.length = length(item);
entire_shaper_item.item.bidiLevel = si.analysis.bidiLevel;
entire_shaper_item.shaperFlags = 0;
if (!kerningEnabled)
@ -487,12 +409,6 @@ void QTextEngine::itemize() const
QSTACKARRAY(QScriptAnalysis, scriptAnalysis, length);
QScriptAnalysis *analysis = scriptAnalysis;
if (option.textDirection() == Qt::RightToLeft) {
for (int i = 0; i < length; ++i)
analysis[i].bidiLevel = 1;
layoutData->hasBidi = true;
}
const ushort *uc = reinterpret_cast<const ushort *>(layoutData->string.unicode());
const ushort *e = uc + length;
QUnicodeTables::Script lastScript = QUnicodeTables::Common;
@ -503,8 +419,6 @@ void QTextEngine::itemize() const
analysis->flags = QScriptAnalysis::Object;
break;
case QChar::LineSeparator:
if (analysis->bidiLevel % 2)
--analysis->bidiLevel;
analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::LineOrParagraphSeparator;
if (option.flags() & QTextOption::ShowLineAndParagraphSeparators)
@ -513,14 +427,12 @@ void QTextEngine::itemize() const
case 9: // Tab
analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::Tab;
analysis->bidiLevel = 0;
break;
case 32: // Space
case QChar::Nbsp:
if (option.flags() & QTextOption::ShowTabsAndSpaces) {
analysis->script = QUnicodeTables::Common;
analysis->flags = QScriptAnalysis::Space;
analysis->bidiLevel = 0;
break;
}
// fall through
@ -578,25 +490,6 @@ void QTextEngine::itemize() const
resolveAdditionalFormats();
}
bool QTextEngine::isRightToLeft() const
{
switch (option.textDirection()) {
case Qt::LeftToRight:
return false;
case Qt::RightToLeft:
return true;
default:
break;
}
if (!layoutData)
itemize();
// this places the cursor in the right position depending on the keyboard layout
if (layoutData->string.isEmpty())
return QApplication::keyboardInputDirection() == Qt::RightToLeft;
return layoutData->string.isRightToLeft();
}
int QTextEngine::findItem(int strPos) const
{
itemize();
@ -1015,8 +908,7 @@ void QTextEngine::justify(const QScriptLine &line)
}
}
QFixed leading = leadingSpaceWidth(line);
QFixed need = line.width - line.textWidth - leading;
QFixed need = line.width - line.textWidth;
if (need < 0) {
// line overflows already!
const_cast<QScriptLine &>(line).justified = true;
@ -1105,7 +997,6 @@ QTextEngine::LayoutData::LayoutData()
allocated = 0;
memory_on_stack = false;
used = 0;
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
logClustersPtr = 0;
@ -1139,7 +1030,6 @@ QTextEngine::LayoutData::LayoutData(const QString &str, void **stack_memory, int
::memset(memory, 0, space_charAttributes * QT_POINTER_SIZE);
}
used = 0;
hasBidi = false;
layoutState = LayoutEmpty;
haveCharAttributes = false;
}
@ -1225,7 +1115,6 @@ void QTextEngine::freeMemory()
layoutData = 0;
} else {
layoutData->used = 0;
layoutData->hasBidi = false;
layoutData->layoutState = LayoutEmpty;
layoutData->haveCharAttributes = false;
}
@ -1566,20 +1455,6 @@ QFixed QTextEngine::calculateTabWidth(int item, QFixed x) const
QList<QTextOption::Tab> tabArray = option.tabs();
if (!tabArray.isEmpty()) {
if (isRightToLeft()) { // rebase the tabArray positions.
QList<QTextOption::Tab> newTabs;
QList<QTextOption::Tab>::Iterator iter = tabArray.begin();
while(iter != tabArray.end()) {
QTextOption::Tab tab = *iter;
if (tab.type == QTextOption::LeftTab)
tab.type = QTextOption::RightTab;
else if (tab.type == QTextOption::RightTab)
tab.type = QTextOption::LeftTab;
newTabs << tab;
++iter;
}
tabArray = newTabs;
}
for (int i = 0; i < tabArray.size(); ++i) {
QFixed tab = QFixed::fromReal(tabArray[i].position) * dpiScale;
if (tab > x) { // this is the tab we need.
@ -1663,16 +1538,6 @@ void QTextEngine::resolveAdditionalFormats() const
specialData->resolvedFormatIndices = indices;
}
QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
{
if (!line.hasTrailingSpaces
|| (option.flags() & QTextOption::IncludeTrailingSpaces)
|| !isRightToLeft())
return QFixed();
return width(line.from + line.length, line.trailingSpaces);
}
QFixed QTextEngine::alignLine(const QScriptLine &line)
{
QFixed x = 0;
@ -1680,8 +1545,6 @@ QFixed QTextEngine::alignLine(const QScriptLine &line)
// if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
if (!line.justified && line.width != QFIXED_MAX) {
int align = option.alignment();
if (align & Qt::AlignJustify && isRightToLeft())
align = Qt::AlignRight;
if (align & Qt::AlignRight)
x = line.width - (line.textAdvance);
else if (align & Qt::AlignHCenter)
@ -1838,25 +1701,16 @@ int QTextEngine::lineNumberForTextPosition(int pos)
void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
{
QTextLineItemIterator iterator(this, lineNum);
bool rtl = isRightToLeft();
bool lastLine = lineNum >= lines.size() - 1;
while (!iterator.atEnd()) {
iterator.next();
const QScriptItem *si = &layoutData->items[iterator.item];
if (si->analysis.bidiLevel % 2) {
int i = iterator.itemEnd - 1, min = iterator.itemStart;
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
i++;
for (; i >= min; i--)
insertionPoints.push_back(i);
} else {
int i = iterator.itemStart, max = iterator.itemEnd;
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
max++;
for (; i < max; i++)
insertionPoints.push_back(i);
}
int i = iterator.itemStart, max = iterator.itemEnd;
if (lastLine && iterator.atEnd())
max++;
for (; i < max; i++)
insertionPoints.push_back(i);
}
}
@ -1886,37 +1740,8 @@ int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation
itemize();
bool moveRight = (op == QTextCursor::Right);
bool alignRight = isRightToLeft();
if (!layoutData->hasBidi)
return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos);
int lineNum = lineNumberForTextPosition(pos);
Q_ASSERT(lineNum >= 0);
QVector<int> insertionPoints;
insertionPointsForLine(lineNum, insertionPoints);
int i, max = insertionPoints.size();
for (i = 0; i < max; i++)
if (pos == insertionPoints[i]) {
if (moveRight) {
if (i + 1 < max)
return insertionPoints[i + 1];
} else {
if (i > 0)
return insertionPoints[i - 1];
}
if (moveRight ^ alignRight) {
if (lineNum + 1 < lines.size())
return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
}
else {
if (lineNum > 0)
return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
}
}
return pos;
return (moveRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos));
}
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
@ -1950,8 +1775,6 @@ void QTextItemInt::initWithScriptItem(const QScriptItem &si)
// explicitly initialize flags so that initFontAttributes can be called
// multiple times on the same TextItem
flags = 0;
if (si.analysis.bidiLevel %2)
flags |= QTextItem::RightToLeft;
ascent = si.ascent;
descent = si.descent;
@ -1984,8 +1807,6 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
logicalItem(-1),
item(-1),
visualOrder(nItems),
levels(nItems),
selection(_selection)
{
pos_x = x = QFixed::fromReal(pos.x());
@ -1994,10 +1815,6 @@ QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, co
x += eng->alignLine(line);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
eng->shapeLine(line);
}
@ -2006,7 +1823,7 @@ QScriptItem &QTextLineItemIterator::next()
x += itemWidth;
++logicalItem;
item = visualOrder[logicalItem] + firstItem;
item = (logicalItem + firstItem);
itemLength = eng->length(item);
si = &eng->layoutData->items[item];
if (!si->num_glyphs)
@ -2068,17 +1885,10 @@ bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selec
int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
QFixed soff;
QFixed swidth;
if (si->analysis.bidiLevel %2) {
for (int g = glyphsEnd - 1; g >= end_glyph; --g)
soff += glyphs.effectiveAdvance(g);
for (int g = end_glyph - 1; g >= start_glyph; --g)
swidth += glyphs.effectiveAdvance(g);
} else {
for (int g = glyphsStart; g < start_glyph; ++g)
soff += glyphs.effectiveAdvance(g);
for (int g = start_glyph; g < end_glyph; ++g)
swidth += glyphs.effectiveAdvance(g);
}
for (int g = glyphsStart; g < start_glyph; ++g)
soff += glyphs.effectiveAdvance(g);
for (int g = start_glyph; g < end_glyph; ++g)
swidth += glyphs.effectiveAdvance(g);
// If the starting character is in the middle of a ligature,
// selection should only contain the right part of that ligature

View file

@ -90,10 +90,9 @@ struct Q_AUTOTEST_EXPORT QScriptAnalysis
Object = 4
};
QUnicodeTables::Script script;
unsigned short bidiLevel; // Unicode Bidi algorithm embedding level (0-61)
Flags flags;
inline bool operator == (const QScriptAnalysis &other) const {
return script == other.script && bidiLevel == other.bidiLevel && flags == other.flags;
return script == other.script && flags == other.flags;
}
};
Q_DECLARE_TYPEINFO(QScriptAnalysis, Q_PRIMITIVE_TYPE);
@ -356,7 +355,6 @@ public:
unsigned short *logClustersPtr;
QGlyphLayout glyphLayout;
mutable int used;
bool hasBidi;
LayoutState layoutState;
bool memory_on_stack;
bool haveCharAttributes;
@ -383,9 +381,6 @@ public:
void validate() const;
void itemize() const;
bool isRightToLeft() const;
static void bidiReorder(int numRuns, const quint8 *levels, int *visualOrder);
const HB_CharAttributes *attributes() const;
void shape(int item) const;
@ -509,7 +504,6 @@ public:
QString elidedText(Qt::TextElideMode mode, const QFixed &width, int flags = 0) const;
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos, bool cursorOnCharacter);
@ -577,9 +571,6 @@ struct QTextLineItemIterator
QFixed itemWidth;
QVarLengthArray<int> visualOrder;
QVarLengthArray<uchar> levels;
const QTextLayout::FormatRange *selection;
};

View file

@ -224,7 +224,7 @@ QTextFormat QTextInlineObject::format() const
*/
Qt::LayoutDirection QTextInlineObject::textDirection() const
{
return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
return Qt::LeftToRight;
}
/*!
@ -1027,7 +1027,6 @@ void QTextLayout::draw(QPainter *p, const QPointF &pos, const QVector<FormatRang
QRectF lineRect(tl.naturalTextRect());
lineRect.translate(position);
lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
bool isLastLineInBlock = (line == d->lines.size()-1);
int sl_length = sl.length + (isLastLineInBlock? 1 : 0); // the infamous newline
@ -1202,14 +1201,12 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
QFixed base = sl.base();
QFixed descent = sl.descent;
bool rightToLeft = d->isRightToLeft();
if (itm >= 0) {
const QScriptItem &si = d->layoutData->items.at(itm);
if (si.ascent > 0)
base = si.ascent;
if (si.descent > 0)
descent = si.descent;
rightToLeft = si.analysis.bidiLevel % 2;
}
qreal y = position.y() + (sl.y + sl.base() - base).toReal();
bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
@ -1219,13 +1216,6 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
p->fillRect(QRectF(x, y, qreal(width), (base + descent + 1).toReal()), p->pen().brush());
if (toggleAntialiasing)
p->setRenderHint(QPainter::Antialiasing, false);
if (d->layoutData->hasBidi) {
const int arrow_extent = 4;
int sign = rightToLeft ? -1 : 1;
p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
}
return;
}
/*!
@ -1966,9 +1956,6 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
if (ul)
while (*ul != -1 && *ul < start)
++ul;
bool rtl = si.analysis.bidiLevel % 2;
if (rtl)
x += si.width;
do {
int gtmp = ge;
@ -1988,12 +1975,9 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
}
start = stmp;
gf.width = w;
if (rtl)
x -= w;
if (gf.num_chars)
p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
if (!rtl)
x += w;
x += w;
if (ul && *ul != -1 && *ul < end) {
// draw underline
gtmp = (*ul == end-1) ? ge : logClusters[*ul+1-si.position];
@ -2010,11 +1994,8 @@ static void drawMenuText(QPainter *p, QFixed x, QFixed y, const QScriptItem &si,
++start;
gf.width = w;
gf.underlineStyle = QTextCharFormat::SingleUnderline;
if (rtl)
x -= w;
p->drawTextItem(QPointF(x.toReal(), y.toReal()), gf);
if (!rtl)
x += w;
x += w;
gf.underlineStyle = QTextCharFormat::NoUnderline;
++gf.chars;
++ul;
@ -2279,7 +2260,7 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
bool lastLine = i >= eng->lines.size() - 1;
QFixed x = line.x;
x += eng->alignLine(line) - eng->leadingSpaceWidth(line);
x += eng->alignLine(line);
if (!i && !eng->layoutData->items.size()) {
*cursorPos = 0;
@ -2326,23 +2307,14 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
glyph_pos++;
}
bool reverse = eng->layoutData->items[itm].analysis.bidiLevel % 2;
// add the items left of the cursor
int firstItem = eng->findItem(line.from);
int lastItem = eng->findItem(lineEnd - 1);
int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
QVarLengthArray<int> visualOrder(nItems);
QVarLengthArray<uchar> levels(nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem;
int item = (i + firstItem);
if (item == itm)
break;
QScriptItem &si = eng->layoutData->items[item];
@ -2372,27 +2344,17 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
logClusters = eng->logClusters(si);
glyphs = eng->shapedGlyphs(si);
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
if (pos == (reverse ? 0 : l))
if (pos == l)
x += si->width;
} else {
bool rtl = eng->isRightToLeft();
bool visual = eng->visualCursorMovement();
int end = qMin(lineEnd, si->position + l) - si->position;
if (reverse) {
int glyph_end = end == l ? si->num_glyphs : logClusters[end];
int glyph_start = glyph_pos;
if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
glyph_start++;
for (int i = glyph_end - 1; i >= glyph_start; i--)
x += glyphs.effectiveAdvance(i);
} else {
int start = qMax(line.from - si->position, 0);
int glyph_start = logClusters[start];
int glyph_end = glyph_pos;
if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
glyph_end--;
for (int i = glyph_start; i <= glyph_end; i++)
x += glyphs.effectiveAdvance(i);
int start = qMax(line.from - si->position, 0);
int glyph_start = logClusters[start];
int glyph_end = glyph_pos;
glyph_end--;
for (int i = glyph_start; i <= glyph_end; i++) {
x += glyphs.effectiveAdvance(i);
}
x += eng->offsetInLigature(si, pos, end, glyph_pos);
}
@ -2441,22 +2403,14 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
x -= eng->alignLine(line);
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
QSTACKARRAY(int, visualOrder, nItems);
QSTACKARRAY(unsigned char, levels, nItems);
for (int i = 0; i < nItems; ++i)
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
QTextEngine::bidiReorder(nItems, levels, visualOrder);
bool visual = eng->visualCursorMovement();
if (x <= 0) {
// left of first item
int item = visualOrder[0]+firstItem;
int item = 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;
@ -2464,15 +2418,11 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|| (line.justified && x < line.width)) {
// has to be in one of the runs
QFixed pos;
bool rtl = eng->isRightToLeft();
eng->shapeLine(line);
QVector<int> insertionPoints;
if (visual && rtl)
eng->insertionPointsForLine(lineNum, insertionPoints);
int nchars = 0;
for (int i = 0; i < nItems; ++i) {
int item = visualOrder[i]+firstItem;
int item = (i + firstItem);
QScriptItem &si = eng->layoutData->items[item];
int item_length = eng->length(item);
// qDebug(" item %d, visual %d x_remain=%f", i, item, x.toReal());
@ -2509,7 +2459,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
return si.position;
bool left_half = (x - pos) < item_width/2;
if (bool(si.analysis.bidiLevel % 2) != left_half)
if (left_half)
return si.position;
return si.position + 1;
}
@ -2518,91 +2468,34 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
QFixed edge;
// has to be inside run
if (cpos == QTextLine::CursorOnCharacter) {
if (si.analysis.bidiLevel % 2) {
pos += item_width;
glyph_pos = gs;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart) {
if (pos < x)
break;
glyph_pos = gs;
edge = pos;
glyph_pos = gs;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart) {
if (pos > x)
break;
}
pos -= glyphs.effectiveAdvance(gs);
++gs;
}
} else {
glyph_pos = gs;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart) {
if (pos > x)
break;
glyph_pos = gs;
edge = pos;
}
pos += glyphs.effectiveAdvance(gs);
++gs;
glyph_pos = gs;
edge = pos;
}
pos += glyphs.effectiveAdvance(gs);
++gs;
}
} else {
QFixed dist = INT_MAX/256;
if (si.analysis.bidiLevel % 2) {
if (!visual || rtl || (lastLine && i == nItems - 1)) {
pos += item_width;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
pos -= glyphs.effectiveAdvance(gs);
++gs;
}
} else {
while (ge >= gs) {
if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = ge;
edge = pos;
dist = qAbs(x-pos);
}
pos += glyphs.effectiveAdvance(ge);
--ge;
}
}
} else {
if (!visual || !rtl || (lastLine && i == 0)) {
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
pos += glyphs.effectiveAdvance(gs);
++gs;
}
} else {
QFixed oldPos = pos;
while (gs <= ge) {
pos += glyphs.effectiveAdvance(gs);
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
++gs;
}
pos = oldPos;
while (gs <= ge) {
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
glyph_pos = gs;
edge = pos;
dist = qAbs(x-pos);
}
pos += glyphs.effectiveAdvance(gs);
++gs;
}
if (qAbs(x-pos) < dist) {
if (visual) {
if (!rtl && i < nItems - 1) {
if (i < nItems - 1) {
nchars += end;
continue;
}
if (rtl && nchars > 0)
return insertionPoints[lastLine ? nchars : nchars - 1];
}
return eng->positionInLigature(&si, end, x, pos, -1,
cpos == QTextLine::CursorOnCharacter);
@ -2615,13 +2508,12 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
}
// right of last item
// qDebug() << "right of last";
int item = visualOrder[nItems-1]+firstItem;
int item = (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 += eng->length(item);
pos = qMax(line.from, pos);
int maxPos = line.from + line_length;