summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Jiang <jiang.jiang@nokia.com>2011-05-19 08:29:49 (GMT)
committerJiang Jiang <jiang.jiang@nokia.com>2011-05-23 13:55:03 (GMT)
commite5f38bb543696813a70995c90cd5450602c0356c (patch)
tree05390f3a3507cc999e05c7526333fd9c5e7f879b
parentd80949eee06ff464d58bd97a6c89bae7e961f3c8 (diff)
downloadQt-e5f38bb543696813a70995c90cd5450602c0356c.zip
Qt-e5f38bb543696813a70995c90cd5450602c0356c.tar.gz
Qt-e5f38bb543696813a70995c90cd5450602c0356c.tar.bz2
Support placing cursor in ligature with mouse or touch
We need to find out the closest element in the ligature to the point we clicked (or tapped), currently we do this by dividing the width of that ligature glyph evenly by the number of characters it covered. We only support Common and Greek script at this point, ligatures in other scripts are still handled as a whole. Task-number: QTBUG-19260 Reviewed-by: Eskil
-rw-r--r--src/gui/text/qtextengine.cpp66
-rw-r--r--src/gui/text/qtextengine_p.h3
-rw-r--r--src/gui/text/qtextlayout.cpp12
-rw-r--r--tests/auto/qtextlayout/tst_qtextlayout.cpp25
4 files changed, 99 insertions, 7 deletions
diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp
index 806779a..62de1fe 100644
--- a/src/gui/text/qtextengine.cpp
+++ b/src/gui/text/qtextengine.cpp
@@ -2697,6 +2697,72 @@ QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
return width(line.from + pos, line.length - pos);
}
+// Scan in logClusters[from..to-1] for glyph_pos
+int QTextEngine::getClusterLength(unsigned short *logClusters,
+ const HB_CharAttributes *attributes,
+ int from, int to, int glyph_pos, int *start)
+{
+ int clusterLength = 0;
+ for (int i = from; i < to; i++) {
+ if (logClusters[i] == glyph_pos && attributes[i].charStop) {
+ if (*start < 0)
+ *start = i;
+ clusterLength++;
+ }
+ else if (clusterLength)
+ break;
+ }
+ return clusterLength;
+}
+
+int QTextEngine::positionInLigature(const QScriptItem *si, int end,
+ QFixed x, QFixed edge, int glyph_pos)
+{
+ unsigned short *logClusters = this->logClusters(si);
+ int clusterStart = -1;
+ int clusterLength = 0;
+
+ if (si->analysis.script != QUnicodeTables::Common &&
+ si->analysis.script != QUnicodeTables::Greek) {
+ if (glyph_pos == -1)
+ return si->position + end;
+ else {
+ int i;
+ for (i = 0; i < end; i++)
+ if (logClusters[i] == glyph_pos)
+ break;
+ return si->position + i;
+ }
+ }
+
+ if (glyph_pos == -1 && end > 0)
+ glyph_pos = logClusters[end - 1];
+ else {
+ if (x < edge)
+ glyph_pos--;
+ }
+
+ const HB_CharAttributes *attrs = attributes();
+ clusterLength = getClusterLength(logClusters, attrs, 0, end, glyph_pos, &clusterStart);
+
+ if (clusterLength) {
+ const QGlyphLayout &glyphs = shapedGlyphs(si);
+ QFixed glyphWidth = glyphs.effectiveAdvance(glyph_pos);
+ // the approximate width of each individual element of the ligature
+ QFixed perItemWidth = glyphWidth / clusterLength;
+ QFixed left = x > edge ? edge : edge - glyphWidth;
+ int n = ((x - left) / perItemWidth).floor().toInt();
+ QFixed dist = x - left - n * perItemWidth;
+ int closestItem = dist > (perItemWidth / 2) ? n + 1 : n;
+ int pos = si->position + clusterStart + closestItem;
+ // Jump to the next charStop
+ while (!attrs[pos].charStop && pos < end)
+ pos++;
+ return pos;
+ }
+ return si->position + end;
+}
+
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
: QTextEngine(string, f),
_layoutData(string, _memory, MemSize)
diff --git a/src/gui/text/qtextengine_p.h b/src/gui/text/qtextengine_p.h
index 5e33f21..f8939e3 100644
--- a/src/gui/text/qtextengine_p.h
+++ b/src/gui/text/qtextengine_p.h
@@ -594,6 +594,8 @@ public:
void shapeLine(const QScriptLine &line);
QFixed leadingSpaceWidth(const QScriptLine &line);
+ int positionInLigature(const QScriptItem *si, int end, QFixed x, QFixed edge, int glyph_pos);
+
private:
void setBoundary(int strPos) const;
void addRequiredBoundaries() const;
@@ -608,6 +610,7 @@ private:
void splitItem(int item, int pos) const;
void resolveAdditionalFormats() const;
+ int getClusterLength(unsigned short *logClusters, const HB_CharAttributes *attributes, int from, int to, int glyph_pos, int *start);
};
class QStackTextEngine : public QTextEngine {
diff --git a/src/gui/text/qtextlayout.cpp b/src/gui/text/qtextlayout.cpp
index de4ca4f..5857f33 100644
--- a/src/gui/text/qtextlayout.cpp
+++ b/src/gui/text/qtextlayout.cpp
@@ -2622,6 +2622,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
}
int glyph_pos = -1;
+ QFixed edge;
// has to be inside run
if (cpos == QTextLine::CursorOnCharacter) {
if (si.analysis.bidiLevel % 2) {
@@ -2632,6 +2633,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
if (pos < x)
break;
glyph_pos = gs;
+ edge = pos;
break;
}
pos -= glyphs.effectiveAdvance(gs);
@@ -2644,6 +2646,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
if (pos > x)
break;
glyph_pos = gs;
+ edge = pos;
}
pos += glyphs.effectiveAdvance(gs);
++gs;
@@ -2672,15 +2675,10 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
}
}
if (qAbs(x-pos) < dist)
- return si.position + end;
+ return eng->positionInLigature(&si, end, x, pos, -1);
}
Q_ASSERT(glyph_pos != -1);
- int j;
- for (j = 0; j < eng->length(item); ++j)
- if (logClusters[j] == glyph_pos)
- break;
-// qDebug("at pos %d (in run: %d)", si.position + j, j);
- return si.position + j;
+ return eng->positionInLigature(&si, end, x, edge, glyph_pos);
}
}
// right of last item
diff --git a/tests/auto/qtextlayout/tst_qtextlayout.cpp b/tests/auto/qtextlayout/tst_qtextlayout.cpp
index 964679a..df84c8f 100644
--- a/tests/auto/qtextlayout/tst_qtextlayout.cpp
+++ b/tests/auto/qtextlayout/tst_qtextlayout.cpp
@@ -127,6 +127,7 @@ private slots:
void textWidthWithLineSeparator();
void textWithSurrogates_qtbug15679();
void cursorInLigatureWithMultipleLines();
+ void xToCursorForLigatures();
private:
QFont testFont;
@@ -1453,5 +1454,29 @@ void tst_QTextLayout::cursorInLigatureWithMultipleLines()
QVERIFY(line.cursorToX(0) != line.cursorToX(1));
}
+void tst_QTextLayout::xToCursorForLigatures()
+{
+#if !defined(Q_WS_MAC)
+ QSKIP("This test can not be run on Mac", SkipAll);
+#endif
+ QTextLayout layout("fi", QFont("Times", 20));
+ layout.beginLayout();
+ QTextLine line = layout.createLine();
+ layout.endLayout();
+
+ QVERIFY(line.xToCursor(0) != line.xToCursor(line.naturalTextWidth() / 2));
+
+ // U+0061 U+0308
+ QTextLayout layout2(QString::fromUtf8("\x61\xCC\x88"), QFont("Times", 20));
+
+ layout2.beginLayout();
+ line = layout2.createLine();
+ layout2.endLayout();
+
+ qreal width = line.naturalTextWidth();
+ QVERIFY(line.xToCursor(0) == line.xToCursor(width / 2) ||
+ line.xToCursor(width) == line.xToCursor(width / 2));
+}
+
QTEST_MAIN(tst_QTextLayout)
#include "tst_qtextlayout.moc"