11#include <qvarlengtharray.h>
12#include <qtextformat.h>
13#include <qabstracttextdocumentlayout.h>
26#include <private/qpainter_p.h>
30#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1
)
31#define SuppressText 0x5012
32#define SuppressBackground 0x513
35
36
37
38
39
40
41
42
43
46
47
48
51
52
53
56
57
58
61
62
63
64
67
68
69
70
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
94
95
96
97
98
99
102
103
104
105
108
109
110
111
112
115
116
117
118
119QRectF QTextInlineObject::rect()
const
121 QScriptItem& si = eng->layoutData->items[itm];
122 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
126
127
128
129
130qreal QTextInlineObject::width()
const
132 return eng->layoutData->items.at(itm).width.toReal();
136
137
138
139
140qreal QTextInlineObject::ascent()
const
142 return eng->layoutData->items.at(itm).ascent.toReal();
146
147
148
149
150qreal QTextInlineObject::descent()
const
152 return eng->layoutData->items.at(itm).descent.toReal();
156
157
158
159
160
161qreal QTextInlineObject::height()
const
163 return eng->layoutData->items.at(itm).height().toReal();
167
168
169
170
171void QTextInlineObject::setWidth(qreal w)
173 eng->layoutData->items[itm].width = QFixed::fromReal(w);
177
178
179
180
181void QTextInlineObject::setAscent(qreal a)
183 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
187
188
189
190
191void QTextInlineObject::setDescent(qreal d)
193 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
197
198
199int QTextInlineObject::textPosition()
const
201 return eng->layoutData->items[itm].position;
205
206
207
208int QTextInlineObject::formatIndex()
const
210 return eng->formatIndex(&eng->layoutData->items[itm]);
214
215
216QTextFormat QTextInlineObject::format()
const
218 return eng->format(&eng->layoutData->items[itm]);
222
223
224Qt::LayoutDirection QTextInlineObject::textDirection()
const
226 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
282
283
284
285
286
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
309
310
311
312
313
316
317
318
319
320QTextLayout::QTextLayout()
321{ d =
new QTextEngine(); }
324
325
326QTextLayout::QTextLayout(
const QString& text)
328 d =
new QTextEngine();
333
334
335
336
337
338
339
340
341
343QTextLayout::QTextLayout(
const QString &text,
const QFont &font,
const QPaintDevice *paintdevice)
345 const QFont f(paintdevice ? QFont(font, paintdevice) : font);
346 d =
new QTextEngine((text.isNull() ? (
const QString&)QString::fromLatin1(
"") : text), f);
350
351
352
353QTextLayout::QTextLayout(
const QTextBlock &block)
355 d =
new QTextEngine();
360
361
362QTextLayout::~QTextLayout()
370
371
372
373
374
375
376void QTextLayout::setRawFont(
const QRawFont &rawFont)
378 d->rawFont = rawFont;
379 d->useRawFont =
true;
380 d->resetFontEngineCache();
385
386
387
388
389
390void QTextLayout::setFont(
const QFont &font)
394 d->useRawFont =
false;
396 d->resetFontEngineCache();
400
401
402
403
404
405QFont QTextLayout::font()
const
411
412
413
414
415
416
417
418
419void QTextLayout::setText(
const QString& string)
427
428
429
430
431QString QTextLayout::text()
const
437
438
439
440
441
442void QTextLayout::setTextOption(
const QTextOption &option)
448
449
450
451
452const QTextOption &QTextLayout::textOption()
const
458
459
460
461
462
463
464void QTextLayout::setPreeditArea(
int position,
const QString &text)
466 if (d->preeditAreaPosition() == position && d->preeditAreaText() == text)
468 d->setPreeditArea(position, text);
470 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
471 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
475
476
477
478
479
480int QTextLayout::preeditAreaPosition()
const
482 return d->preeditAreaPosition();
486
487
488
489
490QString QTextLayout::preeditAreaText()
const
492 return d->preeditAreaText();
496
497
498
499
500
501
502
503void QTextLayout::setFormats(
const QList<FormatRange> &formats)
505 d->setFormats(formats);
507 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
508 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
512
513
514
515
516
517
518QList<QTextLayout::FormatRange> QTextLayout::formats()
const
524
525
526
527
528
529
530void QTextLayout::clearFormats()
532 setFormats(QList<FormatRange>());
536
537
538
539
540
541
542
543
544
545void QTextLayout::setCacheEnabled(
bool enable)
547 d->cacheGlyphs = enable;
551
552
553
554
555
556bool QTextLayout::cacheEnabled()
const
558 return d->cacheGlyphs;
562
563
564
565
566
567
568
569void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
571 d->visualMovement = style == Qt::VisualMoveStyle;
575
576
577
578
579
580Qt::CursorMoveStyle QTextLayout::cursorMoveStyle()
const
582 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
586
587
588
589
590
591
592
593void QTextLayout::beginLayout()
596 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
597 qWarning(
"QTextLayout::beginLayout: Called while already doing layout");
604 d->layoutData->layoutState = QTextEngine::InLayout;
608
609
610
611
612void QTextLayout::endLayout()
615 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
616 qWarning(
"QTextLayout::endLayout: Called without beginLayout()");
620 int l = d->lines.size();
621 if (l && d->lines.at(l-1).length < 0) {
622 QTextLine(l-1, d).setNumColumns(INT_MAX);
624 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
630
631
632
633
634
635
636
637
638void QTextLayout::clearLayout()
644
645
646
647
648
649
650int QTextLayout::nextCursorPosition(
int oldPos, CursorMode mode)
const
652 const QCharAttributes *attributes = d->attributes();
653 int len = d->block.isValid() ? d->block.length() - 1
654 : d->layoutData->string.size();
655 Q_ASSERT(len <= d->layoutData->string.size());
656 if (!attributes || oldPos < 0 || oldPos >= len)
659 if (mode == SkipCharacters) {
661 while (oldPos < len && !attributes[oldPos].graphemeBoundary)
664 if (oldPos < len && d->atWordSeparator(oldPos)) {
666 while (oldPos < len && d->atWordSeparator(oldPos))
669 while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
672 while (oldPos < len && attributes[oldPos].whiteSpace)
680
681
682
683
684
685
686int QTextLayout::previousCursorPosition(
int oldPos, CursorMode mode)
const
688 const QCharAttributes *attributes = d->attributes();
689 int len = d->block.isValid() ? d->block.length() - 1
690 : d->layoutData->string.size();
691 Q_ASSERT(len <= d->layoutData->string.size());
692 if (!attributes || oldPos <= 0 || oldPos > len)
695 if (mode == SkipCharacters) {
697 while (oldPos && !attributes[oldPos].graphemeBoundary)
700 while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
703 if (oldPos && d->atWordSeparator(oldPos-1)) {
705 while (oldPos && d->atWordSeparator(oldPos-1))
708 while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
717
718
719
720
721
722
723int QTextLayout::rightCursorPosition(
int oldPos)
const
725 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
731
732
733
734
735
736
737int QTextLayout::leftCursorPosition(
int oldPos)
const
739 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759bool QTextLayout::isValidCursorPosition(
int pos)
const
761 const QCharAttributes *attributes = d->attributes();
762 if (!attributes || pos < 0 || pos > (
int)d->layoutData->string.size())
764 return attributes[pos].graphemeBoundary;
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783QTextLine QTextLayout::createLine()
786 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
787 qWarning(
"QTextLayout::createLine: Called without layouting");
791 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
794 int l = d->lines.size();
795 if (l && d->lines.at(l-1).length < 0) {
796 QTextLine(l-1, d).setNumColumns(INT_MAX);
797 if (d->maxWidth > QFIXED_MAX / 2) {
798 qWarning(
"QTextLayout: text too long, truncated.");
802 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
803 int strlen = d->layoutData->string.size();
804 if (l && from >= strlen) {
805 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
812 line.justified =
false;
813 line.gridfitted =
false;
815 d->lines.append(line);
816 return QTextLine(l, d);
820
821
822
823
824int QTextLayout::lineCount()
const
826 return d->lines.size();
830
831
832
833
834QTextLine QTextLayout::lineAt(
int i)
const
836 return i < lineCount() ? QTextLine(i, d) : QTextLine();
840
841
842
843
844QTextLine QTextLayout::lineForTextPosition(
int pos)
const
846 int lineNum = d->lineNumberForTextPosition(pos);
847 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
851
852
853
854
855
856
857
858QPointF QTextLayout::position()
const
864
865
866
867
868void QTextLayout::setPosition(
const QPointF &p)
874
875
876QRectF QTextLayout::boundingRect()
const
878 if (d->lines.isEmpty())
882 QFixed xmin = d->lines.at(0).x;
883 QFixed ymin = d->lines.at(0).y;
885 for (
int i = 0; i < d->lines.size(); ++i) {
886 const QScriptLine &si = d->lines.at(i);
887 xmin = qMin(xmin, si.x);
888 ymin = qMin(ymin, si.y);
889 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
890 xmax = qMax(xmax, si.x+lineWidth);
892 ymax = qMax(ymax, si.y+si.height().ceil());
894 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
898
899
900
901
902
903
904
905
906qreal QTextLayout::minimumWidth()
const
908 return d->minWidth.toReal();
912
913
914
915
916
917
918
919
920qreal QTextLayout::maximumWidth()
const
922 return d->maxWidth.toReal();
927
928
929void QTextLayout::setFlags(
int flags)
931 if (flags & Qt::TextJustificationForced) {
932 d->option.setAlignment(Qt::AlignJustify);
933 d->forceJustification =
true;
936 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
937 d->ignoreBidi =
true;
938 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
943 QPainterPath *region,
const QRectF &boundingRect)
945 const QScriptLine &line = eng->lines[lineNumber];
951 const qreal selectionY = pos.y() + line.y.toReal();
952 const qreal lineHeight = line.height().toReal();
954 QFixed lastSelectionX = iterator.x;
955 QFixed lastSelectionWidth;
960 QFixed selectionX, selectionWidth;
961 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
962 if (selectionX == lastSelectionX + lastSelectionWidth) {
963 lastSelectionWidth += selectionWidth;
967 if (lastSelectionWidth > 0) {
968 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
969 region->addRect(rect.toAlignedRect());
972 lastSelectionX = selectionX;
973 lastSelectionWidth = selectionWidth;
976 if (lastSelectionWidth > 0) {
977 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
978 region->addRect(rect.toAlignedRect());
984 return clip.isValid() ? (rect & clip) : rect;
988#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008# if !defined(QT_NO_RAWFONT)
1009QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
int length)
const
1011 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032#if !defined(QT_NO_RAWFONT)
1033QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
1035 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
1040 length = text().size();
1042 QHash<std::pair<QFontEngine *,
int>, QGlyphRun> glyphRunHash;
1043 for (
int i=0; i<d->lines.size(); ++i) {
1044 if (d->lines.at(i).from > from + length)
1046 else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
1047 const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
1048 for (
const QGlyphRun &glyphRun : glyphRuns) {
1049 QRawFont rawFont = glyphRun.rawFont();
1051 QFontEngine *fontEngine = rawFont.d->fontEngine;
1052 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1053 std::pair<QFontEngine *,
int> key(fontEngine,
int(flags));
1055 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1056 if (oldGlyphRun.isEmpty()) {
1057 oldGlyphRun = glyphRun;
1059 QList<quint32> indexes = oldGlyphRun.glyphIndexes();
1060 QList<QPointF> positions = oldGlyphRun.positions();
1061 QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
1062 QRectF boundingRect = oldGlyphRun.boundingRect();
1064 indexes += glyphRun.glyphIndexes();
1065 positions += glyphRun.positions();
1066 stringIndexes += glyphRun.stringIndexes();
1067 boundingRect = boundingRect.united(glyphRun.boundingRect());
1069 oldGlyphRun.setGlyphIndexes(indexes);
1070 oldGlyphRun.setPositions(positions);
1071 oldGlyphRun.setStringIndexes(stringIndexes);
1072 oldGlyphRun.setBoundingRect(boundingRect);
1078 return glyphRunHash.values();
1083
1084
1085
1086
1087void QTextLayout::draw(QPainter *p,
const QPointF &pos,
const QList<FormatRange> &selections,
const QRectF &clip)
const
1089 if (d->lines.isEmpty())
1095 QPointF position = pos + d->position;
1097 QFixed clipy = (INT_MIN/256);
1098 QFixed clipe = (INT_MAX/256);
1099 if (clip.isValid()) {
1100 clipy = QFixed::fromReal(clip.y() - position.y());
1101 clipe = clipy + QFixed::fromReal(clip.height());
1105 int lastLine = d->lines.size();
1106 for (
int i = 0; i < d->lines.size(); ++i) {
1107 const QScriptLine &sl = d->lines.at(i);
1113 if ((sl.y + sl.height()) < clipy) {
1119 QPainterPath excludedRegion;
1120 QPainterPath textDoneRegion;
1121 for (
int i = 0; i < selections.size(); ++i) {
1122 FormatRange selection = selections.at(i);
1123 QPainterPath region;
1124 region.setFillRule(Qt::WindingFill);
1126 for (
int line = firstLine; line < lastLine; ++line) {
1127 const QScriptLine &sl = d->lines.at(line);
1128 QTextLine tl(line, d);
1130 QRectF lineRect(tl.naturalTextRect());
1131 lineRect.translate(position);
1132 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1133 lineRect.setBottom(qCeil(lineRect.bottom()));
1135 bool isLastLineInBlock = (line == d->lines.size()-1);
1136 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1139 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1142 const bool selectionStartInLine = sl.from <= selection.start;
1143 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1145 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1146 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, clip));
1148 region.addRect(clipIfValid(lineRect, clip));
1151 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1152 QRectF fullLineRect(tl.rect());
1153 fullLineRect.translate(position);
1154 fullLineRect.setRight(QFIXED_MAX);
1155 fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
1157 const bool rightToLeft = d->isRightToLeft();
1159 if (!selectionEndInLine) {
1160 region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
1161 : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), clip));
1163 if (!selectionStartInLine) {
1164 region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
1165 : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), clip));
1167 }
else if (!selectionEndInLine
1168 && isLastLineInBlock
1169 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1170 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1171 lineRect.height()/4, lineRect.height()), clip));
1176 const QPen oldPen = p->pen();
1177 const QBrush oldBrush = p->brush();
1179 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1180 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1181 p->drawPath(region);
1184 p->setBrush(oldBrush);
1189 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1190 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1192 if (hasBackground) {
1193 selection.format.setProperty(
ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1196 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1197 selection.format.clearProperty(QTextFormat::OutlinePen);
1202 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1206 p->setClipPath(region, Qt::IntersectClip);
1208 for (
int line = firstLine; line < lastLine; ++line) {
1209 QTextLine l(line, d);
1210 l.draw_internal(p, position, &selection);
1215 textDoneRegion += region;
1218 textDoneRegion -= region;
1221 excludedRegion += region;
1224 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1225 if (!needsTextButNoBackground.isEmpty()){
1227 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1228 FormatRange selection;
1229 selection.start = 0;
1230 selection.length = INT_MAX;
1232 for (
int line = firstLine; line < lastLine; ++line) {
1233 QTextLine l(line, d);
1234 l.draw_internal(p, position, &selection);
1239 if (!excludedRegion.isEmpty()) {
1242 QRectF br = boundingRect().translated(position);
1243 br.setRight(QFIXED_MAX);
1245 br = br.intersected(clip);
1247 path -= excludedRegion;
1248 p->setClipPath(path, Qt::IntersectClip);
1251 for (
int i = firstLine; i < lastLine; ++i) {
1253 l.draw(p, position);
1255 if (!excludedRegion.isEmpty())
1259 if (!d->cacheGlyphs)
1264
1265
1266
1267
1268
1269
1270
1271void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition)
const
1273 drawCursor(p, pos, cursorPosition, 1);
1277
1278
1279
1280
1281
1282
1283void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition,
int width)
const
1285 if (d->lines.isEmpty())
1291 QPointF position = pos + d->position;
1293 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
1294 int line = d->lineNumberForTextPosition(cursorPosition);
1297 if (line >= d->lines.size())
1300 QTextLine l(line, d);
1301 const QScriptLine &sl = d->lines.at(line);
1303 qreal x = position.x() + l.cursorToX(cursorPosition);
1305 QFixed base = sl.base();
1306 QFixed descent = sl.descent;
1307 bool rightToLeft = d->isRightToLeft();
1309 const int realCursorPosition = cursorPosition;
1310 if (d->visualCursorMovement()) {
1311 if (cursorPosition == sl.from + sl.length)
1316 int itm = d->findItem(cursorPosition);
1319 const QScriptItem *si = &d->layoutData->items.at(itm);
1322 if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
1323 int neighborItem = itm;
1324 if (neighborItem > 0 && si->position == realCursorPosition)
1326 else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
1328 const bool onBoundary = neighborItem != itm
1329 && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
1330 if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
1332 si = &d->layoutData->items[itm];
1336 if (si->analysis.flags != QScriptAnalysis::Object) {
1339 if (si->descent > 0)
1340 descent = si->descent;
1342 rightToLeft = si->analysis.bidiLevel % 2;
1344 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1345 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1346 && (p->transform().type() > QTransform::TxTranslate);
1347 if (toggleAntialiasing)
1348 p->setRenderHint(QPainter::Antialiasing);
1349 QPainter::CompositionMode origCompositionMode = p->compositionMode();
1350 if (p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
1351 p->setCompositionMode(QPainter::RasterOp_NotDestination);
1352 const QTransform &deviceTransform = p->deviceTransform();
1353 const qreal xScale = deviceTransform.m11();
1354 if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
1355 p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
1358 const QPen origPen = p->pen();
1359 QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
1360 pen.setCosmetic(
true);
1361 const qreal center = x + qreal(width) / 2;
1363 p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
1366 p->setCompositionMode(origCompositionMode);
1367 if (toggleAntialiasing)
1368 p->setRenderHint(QPainter::Antialiasing,
false);
1369 if (d->layoutData->hasBidi) {
1370 const int arrow_extent = 4;
1371 int sign = rightToLeft ? -1 : 1;
1372 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1373 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1399
1400
1401
1402
1403
1406
1407
1408
1409
1410
1413
1414
1415
1416
1417
1418
1421
1422
1423
1424
1427
1428
1429
1430
1433
1434
1435
1436
1440
1441
1442
1443
1444QRectF QTextLine::rect()
const
1446 const QScriptLine& sl = eng->lines.at(index);
1447 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1451
1452
1453QRectF QTextLine::naturalTextRect()
const
1455 const QScriptLine& sl = eng->lines.at(index);
1456 QFixed x = sl.x + eng->alignLine(sl);
1458 QFixed width = sl.textWidth;
1462 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1466
1467
1468
1469
1470qreal QTextLine::x()
const
1472 return eng->lines.at(index).x.toReal();
1476
1477
1478
1479
1480qreal QTextLine::y()
const
1482 return eng->lines.at(index).y.toReal();
1486
1487
1488
1489
1490qreal QTextLine::width()
const
1492 return eng->lines.at(index).width.toReal();
1497
1498
1499
1500
1501qreal QTextLine::ascent()
const
1503 return eng->lines.at(index).ascent.toReal();
1507
1508
1509
1510
1511qreal QTextLine::descent()
const
1513 return eng->lines.at(index).descent.toReal();
1517
1518
1519
1520
1521
1522
1523qreal QTextLine::height()
const
1525 return eng->lines.at(index).height().ceil().toReal();
1529
1530
1531
1532
1533
1534
1535qreal QTextLine::leading()
const
1537 return eng->lines.at(index).leading.toReal();
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554void QTextLine::setLeadingIncluded(
bool included)
1556 eng->lines[index].leadingIncluded= included;
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570bool QTextLine::leadingIncluded()
const
1572 return eng->lines.at(index).leadingIncluded;
1576
1577
1578
1579
1580qreal QTextLine::naturalTextWidth()
const
1582 return eng->lines.at(index).textWidth.toReal();
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595qreal QTextLine::horizontalAdvance()
const
1597 return eng->lines.at(index).textAdvance.toReal();
1601
1602
1603
1604
1605
1606
1607void QTextLine::setLineWidth(qreal width)
1609 QScriptLine &line = eng->lines[index];
1610 if (!eng->layoutData) {
1611 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1615 line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
1617 && line.textWidth <= line.width
1618 && line.from + line.length == eng->layoutData->string.size())
1625 layout_helper(INT_MAX);
1629
1630
1631
1632
1633
1634
1635void QTextLine::setNumColumns(
int numColumns)
1637 QScriptLine &line = eng->lines[index];
1638 line.width = QFIXED_MAX;
1641 layout_helper(numColumns);
1645
1646
1647
1648
1649
1650
1651
1652void QTextLine::setNumColumns(
int numColumns, qreal alignmentWidth)
1654 QScriptLine &line = eng->lines[index];
1655 line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
1658 layout_helper(numColumns);
1662#define LB_DEBUG qDebug
1664#define LB_DEBUG if (0
) qDebug
1669 struct LineBreakHelper
1671 LineBreakHelper() =
default;
1673 QScriptLine tmpData;
1674 QScriptLine spaceData;
1680 int currentPosition = 0;
1681 glyph_t previousGlyph = 0;
1682 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1685 QFixed currentSoftHyphenWidth;
1686 QFixed commitedSoftHyphenWidth;
1687 QFixed rightBearing;
1688 QFixed minimumRightBearing;
1690 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1691 const unsigned short *logClusters =
nullptr;
1693 bool manualWrap =
false;
1694 bool whiteSpaceOrObject =
true;
1696 bool checkFullOtherwiseExtend(QScriptLine &line);
1698 QFixed calculateNewWidth(
const QScriptLine &line)
const {
1699 return line.textWidth + tmpData.textWidth + spaceData.textWidth
1700 + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
1703 inline glyph_t currentGlyph()
const
1705 Q_ASSERT(currentPosition > 0);
1706 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1708 return glyphs.glyphs[logClusters[currentPosition - 1]];
1711 inline void saveCurrentGlyph()
1714 if (currentPosition > 0 &&
1715 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1716 previousGlyph = currentGlyph();
1717 previousGlyphFontEngine = fontEngine;
1721 inline void calculateRightBearing(
QFontEngine *engine, glyph_t glyph)
1725 engine->getGlyphBearings(glyph,
nullptr, &rb);
1731 rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
1734 inline void calculateRightBearing()
1736 if (currentPosition <= 0)
1738 calculateRightBearing(fontEngine.data(), currentGlyph());
1741 inline void calculateRightBearingForPreviousGlyph()
1743 if (previousGlyph > 0)
1744 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1747 static const QFixed RightBearingNotCalculated;
1749 inline void resetRightBearing()
1751 rightBearing = RightBearingNotCalculated;
1756 inline QFixed negativeRightBearing()
const
1758 if (rightBearing == RightBearingNotCalculated)
1761 return qAbs(rightBearing);
1765Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
1767inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1769 LB_DEBUG(
"possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1771 QFixed newWidth = calculateNewWidth(line);
1772 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1775 const QFixed oldTextWidth = line.textWidth;
1777 line.textWidth += spaceData.textWidth;
1779 line.length += spaceData.length;
1780 tmpData.textWidth = 0;
1782 spaceData.textWidth = 0;
1783 spaceData.length = 0;
1785 if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
1786 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1787 currentSoftHyphenWidth = 0;
1796static inline void addNextCluster(
int &pos,
int end, QScriptLine &line,
int &glyphCount,
1797 const QScriptItem ¤t,
const unsigned short *logClusters,
1798 const QGlyphLayout &glyphs, QFixed *clusterWidth =
nullptr)
1800 int glyphPosition = logClusters[pos];
1804 }
while (pos < end && logClusters[pos] == glyphPosition);
1805 QFixed clusterWid = line.textWidth;
1808 line.textWidth += glyphs.advances[glyphPosition];
1812 Q_ASSERT((pos == end && glyphPosition == current
.num_glyphs) || logClusters[pos] == glyphPosition);
1815 *clusterWidth += (line.textWidth - clusterWid);
1821void QTextLine::layout_helper(
int maxGlyphs)
1823 QScriptLine &line = eng->lines[index];
1825 line.trailingSpaces = 0;
1827 line.hasTrailingSpaces =
false;
1829 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
1830 line.setDefaultHeight(eng);
1834 Q_ASSERT(line.from < eng->layoutData->string.size());
1836 LineBreakHelper lbh;
1838 lbh.maxGlyphs = maxGlyphs;
1840 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1841 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1842 const bool breakWordOrAny = breakany || (wrapMode == QTextOption::WrapAtWordBoundaryOrAnywhere);
1843 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1846 int newItem = eng->findItem(line.from);
1847 Q_ASSERT(newItem >= 0);
1849 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f", line.from, newItem,
int(eng->layoutData->items.size()), line.width.toReal());
1851 Qt::Alignment alignment = eng->option.alignment();
1853 const QCharAttributes *attributes = eng->attributes();
1856 lbh.currentPosition = line.from;
1858 lbh.logClusters = eng->layoutData->logClustersPtr;
1859 lbh.previousGlyph = 0;
1861 bool manuallyWrapped =
false;
1862 bool hasInlineObject =
false;
1863 bool reachedEndOfLine =
false;
1864 QFixed maxInlineObjectHeight = 0;
1866 const bool includeTrailingSpaces = eng->option.flags() & QTextOption::IncludeTrailingSpaces;
1868 while (newItem < eng->layoutData->items.size()) {
1869 lbh.resetRightBearing();
1870 if (newItem != item) {
1872 const QScriptItem ¤t = eng->layoutData->items.at(item);
1873 if (!current.num_glyphs) {
1875 attributes = eng->attributes();
1878 lbh.logClusters = eng->layoutData->logClustersPtr;
1880 lbh.currentPosition = qMax(line.from, current.position);
1881 end = current.position + eng->length(item);
1882 lbh.glyphs = eng->shapedGlyphs(¤t);
1883 QFontEngine *fontEngine = eng->fontEngine(current);
1884 if (lbh.fontEngine != fontEngine) {
1885 lbh.fontEngine = fontEngine;
1886 lbh.minimumRightBearing = qMin(QFixed(),
1887 QFixed::fromReal(fontEngine->minRightBearing()));
1890 const QScriptItem ¤t = eng->layoutData->items.at(item);
1892 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1893 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1895 if (current.analysis.flags != QScriptAnalysis::Object) {
1897 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1898 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1901 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1902 lbh.whiteSpaceOrObject =
true;
1903 if (lbh.checkFullOtherwiseExtend(line))
1906 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1907 QFixed tabWidth = eng->calculateTabWidth(item, x);
1908 attributes = eng->attributes();
1911 lbh.logClusters = eng->layoutData->logClustersPtr;
1912 lbh.glyphs = eng->shapedGlyphs(¤t);
1914 lbh.spaceData.textWidth += tabWidth;
1915 lbh.spaceData.length++;
1918 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1919 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1921 if (lbh.checkFullOtherwiseExtend(line))
1923 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1924 lbh.whiteSpaceOrObject =
true;
1927 if (!line.length && !lbh.tmpData.length)
1928 line.setDefaultHeight(eng);
1929 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1930 if (lbh.checkFullOtherwiseExtend(line))
1933 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1934 current, lbh.logClusters, lbh.glyphs);
1936 lbh.tmpData.length++;
1937 lbh.calculateRightBearingForPreviousGlyph();
1939 line += lbh.tmpData;
1940 manuallyWrapped =
true;
1942 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1943 lbh.whiteSpaceOrObject =
true;
1944 lbh.tmpData.length++;
1946 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1947 QTextInlineObject inlineObject(item, eng);
1948 QTextFormat f = inlineObject.format();
1949 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1950 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1951 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1952 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1953 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1957 lbh.tmpData.textWidth += current.width;
1961 if (lbh.checkFullOtherwiseExtend(line))
1964 hasInlineObject =
true;
1965 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1967 }
else if (attributes[lbh.currentPosition].whiteSpace
1968 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1971 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
1972 lbh.saveCurrentGlyph();
1973 lbh.whiteSpaceOrObject =
true;
1974 while (lbh.currentPosition < end
1975 && attributes[lbh.currentPosition].whiteSpace
1976 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
1977 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
1978 current, lbh.logClusters, lbh.glyphs);
1981 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
1984 lbh.whiteSpaceOrObject =
false;
1985 bool sb_or_ws =
false;
1989 if (lbh.currentPosition == 0
1990 || lbh.previousGlyph == 0
1991 || includeTrailingSpaces
1992 || !attributes[lbh.currentPosition - 1].whiteSpace) {
1993 lbh.saveCurrentGlyph();
1995 QFixed accumulatedTextWidth;
1997 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1998 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2005 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2006 && attributes[lbh.currentPosition].whiteSpace
2007 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2009 if (lbh.currentPosition >= eng->layoutData->string.size()
2011 || attributes[lbh.currentPosition].lineBreak
2012 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2015 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2016 if (breakWordOrAny) {
2017 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2018 accumulatedTextWidth = 0;
2023 }
while (lbh.currentPosition < end);
2024 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2026 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2027 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2028 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2045 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2048 if (sb_or_ws|breakany) {
2056 QFixed previousRightBearing = lbh.rightBearing;
2067 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2068 lbh.calculateRightBearing();
2070 if (lbh.checkFullOtherwiseExtend(line)) {
2075 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2076 lbh.rightBearing = previousRightBearing;
2078 lbh.calculateRightBearingForPreviousGlyph();
2080 line.textWidth += lbh.commitedSoftHyphenWidth;
2085 lbh.saveCurrentGlyph();
2087 if (lbh.currentPosition == end)
2091 reachedEndOfLine =
true;
2092 lbh.checkFullOtherwiseExtend(line);
2093 line.textWidth += lbh.commitedSoftHyphenWidth;
2095 line.textAdvance = line.textWidth;
2098 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2099 lbh.calculateRightBearing();
2102 const QFixed textWidthWithoutBearing = line.textWidth;
2103 line.textWidth += lbh.negativeRightBearing();
2105 if (line.length == 0) {
2106 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2107 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2108 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2109 line += lbh.tmpData;
2112 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2114 if (maxInlineObjectHeight > line.ascent + line.descent) {
2116 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2117 line.ascent += toAdd;
2118 line.descent = maxInlineObjectHeight - line.ascent;
2120 int startItem = eng->findItem(line.from);
2121 int endItem = eng->findItem(line.from + line.length);
2123 endItem = eng->layoutData->items.size();
2124 for (
int item = startItem; item < endItem; ++item) {
2125 QScriptItem ¤t = eng->layoutData->items[item];
2126 if (current.analysis.flags == QScriptAnalysis::Object) {
2127 QTextInlineObject inlineObject(item, eng);
2128 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2129 QFixed height = current.ascent + current.descent;
2131 case QTextCharFormat::AlignTop:
2132 current.ascent = line.ascent;
2133 current.descent = height - line.ascent;
2135 case QTextCharFormat::AlignMiddle:
2136 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2137 current.descent = height - line.ascent;
2139 case QTextCharFormat::AlignBottom:
2140 current.descent = line.descent;
2141 current.ascent = height - line.descent;
2146 Q_ASSERT(line.ascent >= current.ascent);
2147 Q_ASSERT(line.descent >= current.descent);
2153 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2154 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2155 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2157 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2158 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2159 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2160 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2162 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2163 layout_helper(lbh.maxGlyphs);
2164 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2169 if (lbh.manualWrap) {
2170 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2171 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2173 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2175 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2177 : textWidthWithoutBearing;
2178 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2179 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2180 if (!manuallyWrapped) {
2181 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2182 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2184 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2185 if (manuallyWrapped)
2186 eng->layoutData->currentMaxWidth = 0;
2189 line.textWidth += trailingSpace;
2190 if (lbh.spaceData.length) {
2191 line.trailingSpaces = lbh.spaceData.length;
2192 line.hasTrailingSpaces =
true;
2195 line.justified =
false;
2196 line.gridfitted =
false;
2200
2201
2202void QTextLine::setPosition(
const QPointF &pos)
2204 eng->lines[index].x = QFixed::fromReal(pos.x());
2205 eng->lines[index].y = QFixed::fromReal(pos.y());
2209
2210
2211QPointF QTextLine::position()
const
2213 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2224
2225
2226
2227int QTextLine::textStart()
const
2229 return eng->lines.at(index).from;
2233
2234
2235
2236
2237int QTextLine::textLength()
const
2239 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2240 && eng->block.isValid() && index == eng->lines.size()-1) {
2241 return eng->lines.at(index).length - 1;
2243 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2248 QBrush bg = chf.background();
2250 p->fillRect(r.toAlignedRect(), bg);
2255 QBrush c = chf.foreground();
2256 if (c.style() == Qt::NoBrush)
2257 p->setPen(defaultPen);
2259 p->setPen(QPen(c, 0));
2262#if !defined(QT_NO_RAWFONT)
2264 const QString &text,
2267 const QGlyphRun::GlyphRunFlags &flags,
2268 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2270 QFixed selectionWidth,
2273 unsigned short *logClusters,
2277 Q_ASSERT(logClusters !=
nullptr);
2283 int rangeStart = textPosition;
2284 int logClusterIndex = 0;
2285 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2290 int rangeEnd = rangeStart;
2291 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2301 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2302 fontD->setFontEngine(fontEngine);
2304 QVarLengthArray<glyph_t> glyphsArray;
2305 QVarLengthArray<QFixedPoint> positionsArray;
2307 QTextItem::RenderFlags renderFlags;
2308 if (flags.testFlag(QGlyphRun::Overline))
2309 renderFlags |= QTextItem::Overline;
2310 if (flags.testFlag(QGlyphRun::Underline))
2311 renderFlags |= QTextItem::Underline;
2312 if (flags.testFlag(QGlyphRun::StrikeOut))
2313 renderFlags |= QTextItem::StrikeOut;
2314 if (flags.testFlag(QGlyphRun::RightToLeft))
2315 renderFlags |= QTextItem::RightToLeft;
2317 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2319 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2321 qreal fontHeight = font.ascent() + font.descent();
2324 QList<quint32> glyphs;
2325 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2326 glyphs.reserve(glyphsArray.size());
2327 QList<QPointF> positions;
2328 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2329 positions.reserve(glyphsArray.size());
2330 QList<qsizetype> stringIndexes;
2331 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2332 stringIndexes.reserve(glyphsArray.size());
2334 int nextClusterIndex = 0;
2335 int currentClusterIndex = 0;
2336 for (
int i = 0; i < glyphsArray.size(); ++i) {
2337 const int glyphArrayIndex = i + glyphsStart;
2340 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2341 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2342 currentClusterIndex = nextClusterIndex;
2343 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2350 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2351 stringIndexes.append(textPosition + currentClusterIndex);
2354 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2355 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2356 glyphs.append(glyphIndex);
2359 QPointF position = positionsArray.at(i).toPointF() + pos;
2360 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2361 positions.append(position);
2364 maxY = minY = position.y();
2366 minY = qMin(minY, position.y());
2367 maxY = qMax(maxY, position.y());
2371 qreal height = maxY + fontHeight - minY;
2373 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2374 glyphRun.setGlyphIndexes(glyphs);
2375 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2376 glyphRun.setPositions(positions);
2377 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2378 glyphRun.setStringIndexes(stringIndexes);
2379 if (retrievalFlags & QTextLayout::RetrieveString)
2380 glyphRun.setSourceString(text);
2381 glyphRun.setFlags(flags);
2382 glyphRun.setRawFont(font);
2384 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2385 selectionWidth.toReal(), height));
2390# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2413 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2436 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2438 const QScriptLine &line = eng->lines.at(index);
2440 if (line.length == 0)
2441 return QList<QGlyphRun>();
2447 length = textLength();
2450 return QList<QGlyphRun>();
2452 QTextLayout::FormatRange selection;
2453 selection.start = from;
2454 selection.length = length;
2456 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2457 qreal y = line.y.toReal() + line.base().toReal();
2458 QList<QGlyphRun> glyphRuns;
2459 while (!iterator.atEnd()) {
2460 QScriptItem &si = iterator.next();
2461 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2464 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2467 QPointF pos(iterator.x.toReal(), y);
2470 QGlyphRun::GlyphRunFlags flags;
2471 if (!eng->useRawFont) {
2472 font = eng->font(si);
2473 if (font.overline())
2474 flags |= QGlyphRun::Overline;
2475 if (font.underline())
2476 flags |= QGlyphRun::Underline;
2477 if (font.strikeOut())
2478 flags |= QGlyphRun::StrikeOut;
2482 if (si.analysis.bidiLevel % 2) {
2483 flags |= QGlyphRun::RightToLeft;
2487 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2488 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2490 unsigned short *logClusters = eng->logClusters(&si);
2491 int glyphsStart = logClusters[relativeFrom];
2492 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2494 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2495 if (nextGlyphIndex - 1 > glyphsEnd)
2496 glyphsEnd = nextGlyphIndex - 1;
2497 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2498 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2500 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2501 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2503 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2508 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2509 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2510 if (!glyphLayout.attributes[i].dontPrint) {
2511 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2512 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2515 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2516 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2517 if (!glyphLayout.attributes[i].dontPrint) {
2518 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2519 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2524 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2528 iterator.getSelectionBounds(&x, &width);
2530 if (glyphLayout.numGlyphs > 0) {
2531 QFontEngine *mainFontEngine;
2532#ifndef QT_NO_RAWFONT
2533 if (eng->useRawFont && eng->rawFont.isValid())
2534 mainFontEngine= eng->fontEngine(si);
2537 mainFontEngine = font.d->engineForScript(si.analysis.script);
2539 if (mainFontEngine->type() == QFontEngine::Multi) {
2540 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2541 int start = rtl ? glyphLayout.numGlyphs : 0;
2542 int end = start - 1;
2543 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2544 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2545 rtl ? --start : ++end) {
2546 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2550 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2551 multiFontEngine->ensureEngineAt(which);
2553 QGlyphRun::GlyphRunFlags subFlags = flags;
2554 if (start == 0 && startsInsideLigature)
2555 subFlags |= QGlyphRun::SplitLigature;
2558 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2566 glyphsStart + start,
2568 logClusters + relativeFrom,
2569 relativeFrom + si.position,
2570 relativeTo - relativeFrom + 1);
2571 if (!glyphRun.isEmpty())
2572 glyphRuns.append(glyphRun);
2574 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2575 if (!subLayout.attributes[i].dontPrint) {
2576 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2577 pos.rx() += (subLayout.advances[i] + justification).toReal();
2588 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2589 multiFontEngine->ensureEngineAt(which);
2591 QGlyphRun::GlyphRunFlags subFlags = flags;
2592 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2593 subFlags |= QGlyphRun::SplitLigature;
2595 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2603 glyphsStart + start,
2605 logClusters + relativeFrom,
2606 relativeFrom + si.position,
2607 relativeTo - relativeFrom + 1);
2608 if (!glyphRun.isEmpty())
2609 glyphRuns.append(glyphRun);
2611 if (startsInsideLigature || endsInsideLigature)
2612 flags |= QGlyphRun::SplitLigature;
2613 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2623 logClusters + relativeFrom,
2624 relativeFrom + si.position,
2625 relativeTo - relativeFrom + 1);
2626 if (!glyphRun.isEmpty())
2627 glyphRuns.append(glyphRun);
2637
2638
2639
2640
2641void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2643 draw_internal(painter, position,
nullptr);
2646void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2647 const QTextLayout::FormatRange *selection)
const
2649#ifndef QT_NO_RAWFONT
2651 Q_ASSERT(!eng->useRawFont);
2653 const QScriptLine &line = eng->lines[index];
2655 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2659 && selection->start <= line.from
2660 && selection->start + selection->length > line.from) {
2662 const qreal lineHeight = line.height().toReal();
2663 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2664 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2665 drawBackground(p, selection->format, r);
2670 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2671 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2673 if (Q_LIKELY(!xlateToFixedRange))
2676 p->translate(origPos);
2679 QFixed lineBase = line.base();
2680 eng->clearDecorations();
2681 eng->enableDelayDecorations();
2683 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2685 const QTextFormatCollection *formatCollection = eng->formatCollection();
2687 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2689 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2691 format.merge(eng->format(si));
2693 if (suppressColors) {
2694 format.clearForeground();
2695 format.clearBackground();
2696 format.clearProperty(QTextFormat::TextUnderlineColor);
2699 format.merge(selection->format);
2703 QTextLineItemIterator iterator(eng, index, pos, selection);
2704 while (!iterator.atEnd()) {
2705 QScriptItem &si = iterator.next();
2707 if (eng->hasFormats() || selection || formatCollection) {
2708 QTextCharFormat format;
2709 if (formatCollection !=
nullptr)
2710 format = formatCollection->defaultTextFormat();
2711 prepareFormat(format, &si);
2712 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2713 iterator.itemWidth.toReal(), line.height().toReal()));
2718 QPen pen = p->pen();
2720 QTextLineItemIterator iterator(eng, index, pos, selection);
2721 while (!iterator.atEnd()) {
2722 QScriptItem &si = iterator.next();
2724 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2727 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2728 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2731 QFixed itemBaseLine = y;
2732 QFont f = eng->font(si);
2733 QTextCharFormat format;
2734 if (formatCollection !=
nullptr)
2735 format = formatCollection->defaultTextFormat();
2737 if (eng->hasFormats() || selection || formatCollection) {
2738 prepareFormat(format, &si);
2739 setPen(p, pen, format);
2741 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2742 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2743 if (valign == QTextCharFormat::AlignSuperScript
2744 || valign == QTextCharFormat::AlignSubScript
2745 || !qFuzzyIsNull(baseLineOffset))
2747 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2748 QFixed height = fe->ascent() + fe->descent();
2749 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2751 if (valign == QTextCharFormat::AlignSubScript)
2752 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2753 else if (valign == QTextCharFormat::AlignSuperScript)
2754 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2758 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2760 if (eng->hasFormats()) {
2762 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2763 QFixed itemY = y - si.ascent;
2764 switch (format.verticalAlignment()) {
2765 case QTextCharFormat::AlignTop:
2766 itemY = y - lineBase;
2768 case QTextCharFormat::AlignMiddle:
2769 itemY = y - lineBase + (line.height() - si.height()) / 2;
2771 case QTextCharFormat::AlignBottom:
2772 itemY = y - lineBase + line.height() - si.height();
2778 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2780 eng->docLayout()->drawInlineObject(p, itemRect,
2781 QTextInlineObject(iterator.item, eng),
2782 si.position + eng->block.position(),
2786 if (bg.style() != Qt::NoBrush) {
2787 QColor c = bg.color();
2789 p->fillRect(itemRect, c);
2793 QFont f = eng->font(si);
2794 QTextItemInt gf(si, &f, format);
2797 gf.width = iterator.itemWidth;
2798 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2799 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2800 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2801 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2802 qreal x = iterator.itemWidth.toReal() - w;
2804 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2805 iterator.itemWidth.toReal(), line.height().toReal()),
2810 p->drawText(QPointF(iterator.x.toReal() + x,
2811 y.toReal()), visualTab);
2821 unsigned short *logClusters = eng->logClusters(&si);
2822 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2824 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2825 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2826 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2827 gf.logClusters = logClusters + iterator.itemStart - si.position;
2828 gf.width = iterator.itemWidth;
2829 gf.justified = line.justified;
2830 gf.initWithScriptItem(si);
2832 Q_ASSERT(gf.fontEngine);
2834 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2835 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2837 path.setFillRule(Qt::WindingFill);
2839 if (gf.glyphs.numGlyphs)
2840 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2842 const QFontEngine *fe = gf.fontEngine;
2843 const qreal lw = fe->lineThickness().toReal();
2844 if (gf.flags & QTextItem::Underline) {
2845 qreal offs = fe->underlinePosition().toReal();
2846 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2848 if (gf.flags & QTextItem::Overline) {
2849 qreal offs = fe->ascent().toReal() + 1;
2850 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2852 if (gf.flags & QTextItem::StrikeOut) {
2853 qreal offs = fe->ascent().toReal() / 3;
2854 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2859 p->setRenderHint(QPainter::Antialiasing);
2862 if (p->pen().style() == Qt::NoPen)
2863 p->setBrush(Qt::NoBrush);
2865 p->setBrush(p->pen().brush());
2867 p->setPen(format.textOutline());
2872 gf.glyphs.numGlyphs = 0;
2873 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2876 if ((si.analysis.flags == QScriptAnalysis::Space
2877 || si.analysis.flags == QScriptAnalysis::Nbsp)
2878 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2879 QBrush c = format.foreground();
2880 if (c.style() != Qt::NoBrush)
2881 p->setPen(c.color());
2882 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2883 QFont oldFont = p->font();
2884 p->setFont(eng->font(si));
2885 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2887 p->setFont(oldFont);
2891 eng->drawDecorations(p);
2893 if (xlateToFixedRange)
2894 p->translate(-origPos);
2896 if (eng->hasFormats())
2901
2902
2903
2904
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2918 const QScriptLine &line = eng->lines[index];
2919 bool lastLine = index >= eng->lines.size() - 1;
2921 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2923 if (!eng->layoutData)
2925 if (!eng->layoutData->items.size()) {
2926 *cursorPos = line.from;
2930 int lineEnd = line.from + line.length + line.trailingSpaces;
2931 int pos = qBound(line.from, *cursorPos, lineEnd);
2932 const QCharAttributes *attributes = eng->attributes();
2934 *cursorPos = line.from;
2937 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2940 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2942 *cursorPos = line.from;
2945 eng->shapeLine(line);
2947 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2948 if (!scriptItem->num_glyphs)
2951 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2954 int neighborItem = itm;
2955 if (neighborItem > 0 && scriptItem->position == pos)
2957 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2959 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2962 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2964 scriptItem = &eng->layoutData->items[itm];
2965 if (!scriptItem->num_glyphs)
2971 const int l = eng->length(itm);
2972 pos = qBound(0, pos - scriptItem->position, l);
2974 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
2975 unsigned short *logClusters = eng->logClusters(scriptItem);
2976 Q_ASSERT(logClusters);
2978 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
2979 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
2982 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
2986 bool reverse = scriptItem->analysis.bidiLevel % 2;
2991 int firstItem = eng->findItem(line.from);
2992 int lastItem = eng->findItem(lineEnd - 1, itm);
2993 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
2995 QVarLengthArray<
int> visualOrder(nItems);
2996 QVarLengthArray<uchar> levels(nItems);
2997 for (
int i = 0; i < nItems; ++i)
2998 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
2999 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3001 for (
int i = 0; i < nItems; ++i) {
3002 int item = visualOrder[i]+firstItem;
3005 QScriptItem &si = eng->layoutData->items[item];
3009 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3014 const int itemLength = eng->length(item);
3015 int start = qMax(line.from, si.position);
3016 int end = qMin(lineEnd, si.position + itemLength);
3018 logClusters = eng->logClusters(&si);
3020 int gs = logClusters[start-si.position];
3021 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3023 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3026 x += glyphs.effectiveAdvance(gs);
3031 logClusters = eng->logClusters(scriptItem);
3032 glyphs = eng->shapedGlyphs(scriptItem);
3033 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3034 if (pos == (reverse ? 0 : l))
3035 x += scriptItem->width;
3037 bool rtl = eng->isRightToLeft();
3038 bool visual = eng->visualCursorMovement();
3039 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3041 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3042 int glyph_start = glyph_pos;
3043 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3045 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3046 x += glyphs.effectiveAdvance(i);
3047 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3049 int start = qMax(line.from - scriptItem->position, 0);
3050 int glyph_start = logClusters[start];
3051 int glyph_end = glyph_pos;
3052 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3054 for (
int i = glyph_start; i <= glyph_end; i++)
3055 x += glyphs.effectiveAdvance(i);
3056 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3060 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3061 x = line.x + line.width;
3062 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3065 *cursorPos = pos + scriptItem->position;
3070
3071
3072
3073
3074
3075
3076
3077
3078int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3080 QFixed x = QFixed::fromReal(_x);
3081 const QScriptLine &line = eng->lines[index];
3082 bool lastLine = index >= eng->lines.size() - 1;
3083 int lineNum = index;
3085 if (!eng->layoutData)
3088 int line_length = textLength();
3093 int firstItem = eng->findItem(line.from);
3094 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3095 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3101 x -= eng->alignLine(line);
3104 QVarLengthArray<
int> visualOrder(nItems);
3105 QVarLengthArray<
unsigned char> levels(nItems);
3106 for (
int i = 0; i < nItems; ++i)
3107 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3108 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3110 bool visual = eng->visualCursorMovement();
3113 if (eng->isRightToLeft())
3114 return line.from + line_length;
3116 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3119 bool rtl = eng->isRightToLeft();
3121 eng->shapeLine(line);
3122 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3124 for (
int i = 0; i < nItems; ++i) {
3125 int item = visualOrder[i]+firstItem;
3126 QScriptItem &si = eng->layoutData->items[item];
3127 int item_length = eng->length(item);
3130 int start = qMax(line.from - si.position, 0);
3131 int end = qMin(line.from + line_length - si.position, item_length);
3133 unsigned short *logClusters = eng->logClusters(&si);
3135 int gs = logClusters[start];
3136 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3137 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3139 QFixed item_width = 0;
3140 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3141 item_width = si.width;
3145 item_width += glyphs.effectiveAdvance(g);
3151 if (pos + item_width < x) {
3157 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3158 if (cpos == QTextLine::CursorOnCharacter)
3160 bool left_half = (x - pos) < item_width/2;
3162 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3164 return si.position + 1;
3170 if (cpos == QTextLine::CursorOnCharacter) {
3171 if (si.analysis.bidiLevel % 2) {
3175 if (glyphs.attributes[gs].clusterStart) {
3181 pos -= glyphs.effectiveAdvance(gs);
3187 if (glyphs.attributes[gs].clusterStart) {
3193 pos += glyphs.effectiveAdvance(gs);
3198 QFixed dist = INT_MAX/256;
3199 if (si.analysis.bidiLevel % 2) {
3200 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3203 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3208 pos -= glyphs.effectiveAdvance(gs);
3213 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3218 pos += glyphs.effectiveAdvance(ge);
3223 if (!visual || !rtl || (lastLine && i == 0)) {
3225 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3230 pos += glyphs.effectiveAdvance(gs);
3234 QFixed oldPos = pos;
3236 pos += glyphs.effectiveAdvance(gs);
3237 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3247 if (qAbs(x-pos) < dist) {
3249 if (!rtl && i < nItems - 1) {
3253 if (rtl && nchars > 0)
3254 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3256 return eng->positionInLigature(&si, end, x, pos, -1,
3257 cpos == QTextLine::CursorOnCharacter);
3260 Q_ASSERT(glyph_pos != -1);
3261 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3262 cpos == QTextLine::CursorOnCharacter);
3266 int pos = line.from;
3267 if (!eng->isRightToLeft())
3274 if (index < eng->lines.size() - 1)
3275 pos = qMin(eng->previousLogicalPosition(pos), pos);
Combined button and popup list for selecting options.
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection, QPainterPath *region, const QRectF &boundingRect)
static void addNextCluster(int &pos, int end, QScriptLine &line, int &glyphCount, const QScriptItem ¤t, const unsigned short *logClusters, const QGlyphLayout &glyphs, QFixed *clusterWidth=nullptr)
static QRectF clipIfValid(const QRectF &rect, const QRectF &clip)
static QGlyphRun glyphRunWithInfo(QFontEngine *fontEngine, const QString &text, const QGlyphLayout &glyphLayout, const QPointF &pos, const QGlyphRun::GlyphRunFlags &flags, QTextLayout::GlyphRunRetrievalFlags retrievalFlags, QFixed selectionX, QFixed selectionWidth, int glyphsStart, int glyphsEnd, unsigned short *logClusters, int textPosition, int textLength)
static void drawBackground(QPainter *p, const QTextCharFormat &chf, const QRectF &r)
#define SuppressBackground
static void setPen(QPainter *p, const QPen &defaultPen, const QTextCharFormat &chf)
#define ObjectSelectionBrush
QGlyphAttributes * attributes
unsigned short num_glyphs