13#include <qvarlengtharray.h>
14#include <qtextformat.h>
15#include <qabstracttextdocumentlayout.h>
28#include <private/qpainter_p.h>
32#define ObjectSelectionBrush (QTextFormat::ForegroundBrush + 1
)
33#define SuppressText 0x5012
34#define SuppressBackground 0x513
37
38
39
40
41
42
43
44
45
48
49
50
53
54
55
58
59
60
63
64
65
66
69
70
71
72
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
96
97
98
99
100
101
104
105
106
107
110
111
112
113
114
117
118
119
120
121QRectF QTextInlineObject::rect()
const
123 QScriptItem& si = eng->layoutData->items[itm];
124 return QRectF(0, -si.ascent.toReal(), si.width.toReal(), si.height().toReal());
128
129
130
131
132qreal QTextInlineObject::width()
const
134 return eng->layoutData->items.at(itm).width.toReal();
138
139
140
141
142qreal QTextInlineObject::ascent()
const
144 return eng->layoutData->items.at(itm).ascent.toReal();
148
149
150
151
152qreal QTextInlineObject::descent()
const
154 return eng->layoutData->items.at(itm).descent.toReal();
158
159
160
161
162
163qreal QTextInlineObject::height()
const
165 return eng->layoutData->items.at(itm).height().toReal();
169
170
171
172
173void QTextInlineObject::setWidth(qreal w)
175 eng->layoutData->items[itm].width = QFixed::fromReal(w);
179
180
181
182
183void QTextInlineObject::setAscent(qreal a)
185 eng->layoutData->items[itm].ascent = QFixed::fromReal(a);
189
190
191
192
193void QTextInlineObject::setDescent(qreal d)
195 eng->layoutData->items[itm].descent = QFixed::fromReal(d);
199
200
201int QTextInlineObject::textPosition()
const
203 return eng->layoutData->items[itm].position;
207
208
209
210int QTextInlineObject::formatIndex()
const
212 return eng->formatIndex(&eng->layoutData->items[itm]);
216
217
218QTextFormat QTextInlineObject::format()
const
220 return eng->format(&eng->layoutData->items[itm]);
224
225
226Qt::LayoutDirection QTextInlineObject::textDirection()
const
228 return (eng->layoutData->items[itm].analysis.bidiLevel % 2 ? Qt::RightToLeft : Qt::LeftToRight);
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
280
281
284
285
286
287
288
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
311
312
313
314
315
318
319
320
321
322QTextLayout::QTextLayout()
323{ d =
new QTextEngine(); }
326
327
328QTextLayout::QTextLayout(
const QString& text)
330 d =
new QTextEngine();
335
336
337
338
339
340
341
342
343
345QTextLayout::QTextLayout(
const QString &text,
const QFont &font,
const QPaintDevice *paintdevice)
347 const QFont f(paintdevice ? QFont(font, paintdevice) : font);
348 d =
new QTextEngine((text.isNull() ? (
const QString&)QString::fromLatin1(
"") : text), f);
352
353
354
355QTextLayout::QTextLayout(
const QTextBlock &block)
357 d =
new QTextEngine();
362
363
364QTextLayout::~QTextLayout()
372
373
374
375
376
377
378void QTextLayout::setRawFont(
const QRawFont &rawFont)
380 d->rawFont = rawFont;
381 d->useRawFont =
true;
382 d->resetFontEngineCache();
387
388
389
390
391
392void QTextLayout::setFont(
const QFont &font)
396 d->useRawFont =
false;
398 d->resetFontEngineCache();
402
403
404
405
406
407QFont QTextLayout::font()
const
413
414
415
416
417
418
419
420
421void QTextLayout::setText(
const QString& string)
429
430
431
432
433QString QTextLayout::text()
const
439
440
441
442
443
444void QTextLayout::setTextOption(
const QTextOption &option)
450
451
452
453
454const QTextOption &QTextLayout::textOption()
const
460
461
462
463
464
465
466void QTextLayout::setPreeditArea(
int position,
const QString &text)
468 if (d->preeditAreaPosition() == position && d->preeditAreaText() == text)
470 d->setPreeditArea(position, text);
472 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
473 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
477
478
479
480
481
482int QTextLayout::preeditAreaPosition()
const
484 return d->preeditAreaPosition();
488
489
490
491
492QString QTextLayout::preeditAreaText()
const
494 return d->preeditAreaText();
498
499
500
501
502
503
504
505void QTextLayout::setFormats(
const QList<FormatRange> &formats)
507 d->setFormats(formats);
509 if (QTextDocumentPrivate::get(d->block) !=
nullptr)
510 QTextDocumentPrivate::get(d->block)->documentChange(d->block.position(), d->block.length());
514
515
516
517
518
519
520QList<QTextLayout::FormatRange> QTextLayout::formats()
const
526
527
528
529
530
531
532void QTextLayout::clearFormats()
534 setFormats(QList<FormatRange>());
538
539
540
541
542
543
544
545
546
547void QTextLayout::setCacheEnabled(
bool enable)
549 d->cacheGlyphs = enable;
553
554
555
556
557
558bool QTextLayout::cacheEnabled()
const
560 return d->cacheGlyphs;
564
565
566
567
568
569
570
571void QTextLayout::setCursorMoveStyle(Qt::CursorMoveStyle style)
573 d->visualMovement = style == Qt::VisualMoveStyle;
577
578
579
580
581
582Qt::CursorMoveStyle QTextLayout::cursorMoveStyle()
const
584 return d->visualMovement ? Qt::VisualMoveStyle : Qt::LogicalMoveStyle;
588
589
590
591
592
593
594
595void QTextLayout::beginLayout()
598 if (d->layoutData && d->layoutData->layoutState == QTextEngine::InLayout) {
599 qWarning(
"QTextLayout::beginLayout: Called while already doing layout");
606 d->layoutData->layoutState = QTextEngine::InLayout;
610
611
612
613
614void QTextLayout::endLayout()
617 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
618 qWarning(
"QTextLayout::endLayout: Called without beginLayout()");
622 int l = d->lines.size();
623 if (l && d->lines.at(l-1).length < 0) {
624 QTextLine(l-1, d).setNumColumns(INT_MAX);
626 d->layoutData->layoutState = QTextEngine::LayoutEmpty;
632
633
634
635
636
637
638
639
640void QTextLayout::clearLayout()
646
647
648
649
650
651
652int QTextLayout::nextCursorPosition(
int oldPos, CursorMode mode)
const
654 const QCharAttributes *attributes = d->attributes();
655 int len = d->block.isValid() ? d->block.length() - 1
656 : d->layoutData->string.size();
657 Q_ASSERT(len <= d->layoutData->string.size());
658 if (!attributes || oldPos < 0 || oldPos >= len)
661 if (mode == SkipCharacters) {
663 while (oldPos < len && !attributes[oldPos].graphemeBoundary)
666 if (oldPos < len && d->atWordSeparator(oldPos)) {
668 while (oldPos < len && d->atWordSeparator(oldPos))
671 while (oldPos < len && !attributes[oldPos].whiteSpace && !d->atWordSeparator(oldPos))
674 while (oldPos < len && attributes[oldPos].whiteSpace)
682
683
684
685
686
687
688int QTextLayout::previousCursorPosition(
int oldPos, CursorMode mode)
const
690 const QCharAttributes *attributes = d->attributes();
691 int len = d->block.isValid() ? d->block.length() - 1
692 : d->layoutData->string.size();
693 Q_ASSERT(len <= d->layoutData->string.size());
694 if (!attributes || oldPos <= 0 || oldPos > len)
697 if (mode == SkipCharacters) {
699 while (oldPos && !attributes[oldPos].graphemeBoundary)
702 while (oldPos > 0 && attributes[oldPos - 1].whiteSpace)
705 if (oldPos && d->atWordSeparator(oldPos-1)) {
707 while (oldPos && d->atWordSeparator(oldPos-1))
710 while (oldPos > 0 && !attributes[oldPos - 1].whiteSpace && !d->atWordSeparator(oldPos-1))
719
720
721
722
723
724
725int QTextLayout::rightCursorPosition(
int oldPos)
const
727 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
733
734
735
736
737
738
739int QTextLayout::leftCursorPosition(
int oldPos)
const
741 int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761bool QTextLayout::isValidCursorPosition(
int pos)
const
763 const QCharAttributes *attributes = d->attributes();
764 if (!attributes || pos < 0 || pos > (
int)d->layoutData->string.size())
766 return attributes[pos].graphemeBoundary;
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785QTextLine QTextLayout::createLine()
788 if (!d->layoutData || d->layoutData->layoutState == QTextEngine::LayoutEmpty) {
789 qWarning(
"QTextLayout::createLine: Called without layouting");
793 if (d->layoutData->layoutState == QTextEngine::LayoutFailed)
796 int l = d->lines.size();
797 if (l && d->lines.at(l-1).length < 0) {
798 QTextLine(l-1, d).setNumColumns(INT_MAX);
799 if (d->maxWidth > QFIXED_MAX / 2) {
800 qWarning(
"QTextLayout: text too long, truncated.");
804 int from = l > 0 ? d->lines.at(l-1).from + d->lines.at(l-1).length + d->lines.at(l-1).trailingSpaces : 0;
805 int strlen = d->layoutData->string.size();
806 if (l && from >= strlen) {
807 if (!d->lines.at(l-1).length || d->layoutData->string.at(strlen - 1) != QChar::LineSeparator)
814 line.justified =
false;
815 line.gridfitted =
false;
817 d->lines.append(line);
818 return QTextLine(l, d);
822
823
824
825
826int QTextLayout::lineCount()
const
828 return d->lines.size();
832
833
834
835
836QTextLine QTextLayout::lineAt(
int i)
const
838 return i < lineCount() ? QTextLine(i, d) : QTextLine();
842
843
844
845
846QTextLine QTextLayout::lineForTextPosition(
int pos)
const
848 int lineNum = d->lineNumberForTextPosition(pos);
849 return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
853
854
855
856
857
858
859
860QPointF QTextLayout::position()
const
866
867
868
869
870void QTextLayout::setPosition(
const QPointF &p)
876
877
878QRectF QTextLayout::boundingRect()
const
880 if (d->lines.isEmpty())
884 QFixed xmin = d->lines.at(0).x;
885 QFixed ymin = d->lines.at(0).y;
887 for (
int i = 0; i < d->lines.size(); ++i) {
888 const QScriptLine &si = d->lines.at(i);
889 xmin = qMin(xmin, si.x);
890 ymin = qMin(ymin, si.y);
891 QFixed lineWidth = si.width < QFIXED_MAX ? qMax(si.width, si.textWidth) : si.textWidth;
892 xmax = qMax(xmax, si.x+lineWidth);
894 ymax = qMax(ymax, si.y+si.height().ceil());
896 return QRectF(xmin.toReal(), ymin.toReal(), (xmax-xmin).toReal(), (ymax-ymin).toReal());
900
901
902
903
904
905
906
907
908qreal QTextLayout::minimumWidth()
const
910 return d->minWidth.toReal();
914
915
916
917
918
919
920
921
922qreal QTextLayout::maximumWidth()
const
924 return d->maxWidth.toReal();
929
930
931void QTextLayout::setFlags(
int flags)
933 if (flags & Qt::TextJustificationForced) {
934 d->option.setAlignment(Qt::AlignJustify);
935 d->forceJustification =
true;
938 if (flags & (Qt::TextForceLeftToRight|Qt::TextForceRightToLeft)) {
939 d->ignoreBidi =
true;
940 d->option.setTextDirection((flags & Qt::TextForceLeftToRight) ? Qt::LeftToRight : Qt::RightToLeft);
945 QPainterPath *region,
const QRectF &boundingRect)
947 const QScriptLine &line = eng->lines[lineNumber];
953 const qreal selectionY = pos.y() + line.y.toReal();
954 const qreal lineHeight = line.height().toReal();
956 QFixed lastSelectionX = iterator.x;
957 QFixed lastSelectionWidth;
962 QFixed selectionX, selectionWidth;
963 if (iterator.getSelectionBounds(&selectionX, &selectionWidth)) {
964 if (selectionX == lastSelectionX + lastSelectionWidth) {
965 lastSelectionWidth += selectionWidth;
969 if (lastSelectionWidth > 0) {
970 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
971 region->addRect(rect.toAlignedRect());
974 lastSelectionX = selectionX;
975 lastSelectionWidth = selectionWidth;
978 if (lastSelectionWidth > 0) {
979 const QRectF rect = boundingRect & QRectF(lastSelectionX.toReal(), selectionY, lastSelectionWidth.toReal(), lineHeight);
980 region->addRect(rect.toAlignedRect());
986 return clip.isValid() ? (rect & clip) : rect;
990#if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010# if !defined(QT_NO_RAWFONT)
1011QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
int length)
const
1013 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034#if !defined(QT_NO_RAWFONT)
1035QList<QGlyphRun> QTextLayout::glyphRuns(
int from,
1037 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
1042 length = text().size();
1044 QHash<std::pair<QFontEngine *,
int>, QGlyphRun> glyphRunHash;
1045 for (
int i=0; i<d->lines.size(); ++i) {
1046 if (d->lines.at(i).from > from + length)
1048 else if (d->lines.at(i).from + d->lines.at(i).length >= from) {
1049 const QList<QGlyphRun> glyphRuns = QTextLine(i, d).glyphRuns(from, length, retrievalFlags);
1050 for (
const QGlyphRun &glyphRun : glyphRuns) {
1051 QRawFont rawFont = glyphRun.rawFont();
1053 QFontEngine *fontEngine = rawFont.d->fontEngine;
1054 QGlyphRun::GlyphRunFlags flags = glyphRun.flags();
1055 std::pair<QFontEngine *,
int> key(fontEngine,
int(flags));
1057 QGlyphRun &oldGlyphRun = glyphRunHash[key];
1058 if (oldGlyphRun.isEmpty()) {
1059 oldGlyphRun = glyphRun;
1061 QList<quint32> indexes = oldGlyphRun.glyphIndexes();
1062 QList<QPointF> positions = oldGlyphRun.positions();
1063 QList<qsizetype> stringIndexes = oldGlyphRun.stringIndexes();
1064 QRectF boundingRect = oldGlyphRun.boundingRect();
1066 indexes += glyphRun.glyphIndexes();
1067 positions += glyphRun.positions();
1068 stringIndexes += glyphRun.stringIndexes();
1069 boundingRect = boundingRect.united(glyphRun.boundingRect());
1071 oldGlyphRun.setGlyphIndexes(indexes);
1072 oldGlyphRun.setPositions(positions);
1073 oldGlyphRun.setStringIndexes(stringIndexes);
1074 oldGlyphRun.setBoundingRect(boundingRect);
1080 return glyphRunHash.values();
1085
1086
1087
1088
1089void QTextLayout::draw(QPainter *p,
const QPointF &pos,
const QList<FormatRange> &selections,
const QRectF &clip)
const
1091 if (d->lines.isEmpty())
1097 QPointF position = pos + d->position;
1099 QFixed clipy = (INT_MIN/256);
1100 QFixed clipe = (INT_MAX/256);
1101 if (clip.isValid()) {
1102 clipy = QFixed::fromReal(clip.y() - position.y());
1103 clipe = clipy + QFixed::fromReal(clip.height());
1107 int lastLine = d->lines.size();
1108 for (
int i = 0; i < d->lines.size(); ++i) {
1109 const QScriptLine &sl = d->lines.at(i);
1115 if ((sl.y + sl.height()) < clipy) {
1121 QPainterPath excludedRegion;
1122 QPainterPath textDoneRegion;
1123 for (
int i = 0; i < selections.size(); ++i) {
1124 FormatRange selection = selections.at(i);
1127 const QPen pen = selection.format.penProperty(QTextFormat::OutlinePen);
1128 const bool hasOutline = (pen.style() != Qt::NoPen && pen.widthF() > 0);
1129 const QRectF selectionClip = hasOutline ? QRectF() : clip;
1130 QPainterPath region;
1131 region.setFillRule(Qt::WindingFill);
1133 for (
int line = firstLine; line < lastLine; ++line) {
1134 const QScriptLine &sl = d->lines.at(line);
1135 QTextLine tl(line, d);
1137 QRectF lineRect(tl.naturalTextRect());
1138 lineRect.translate(position);
1139 lineRect.adjust(0, 0, d->leadingSpaceWidth(sl).toReal(), 0);
1140 lineRect.setBottom(qCeil(lineRect.bottom()));
1142 bool isLastLineInBlock = (line == d->lines.size()-1);
1143 int sl_length = sl.length + (isLastLineInBlock? 1 : 0);
1146 if (sl.from > selection.start + selection.length || sl.from + sl_length <= selection.start)
1149 const bool selectionStartInLine = sl.from <= selection.start;
1150 const bool selectionEndInLine = selection.start + selection.length < sl.from + sl_length;
1152 if (sl.length && (selectionStartInLine || selectionEndInLine)) {
1153 addSelectedRegionsToPath(d, line, position, &selection, ®ion, clipIfValid(lineRect, selectionClip));
1155 region.addRect(clipIfValid(lineRect, selectionClip));
1158 if (selection.format.boolProperty(QTextFormat::FullWidthSelection)) {
1159 QRectF fullLineRect(tl.rect());
1160 fullLineRect.translate(position);
1161 fullLineRect.setRight(QFIXED_MAX);
1162 fullLineRect.setBottom(qCeil(fullLineRect.bottom()));
1164 const bool rightToLeft = d->isRightToLeft();
1166 if (!selectionEndInLine) {
1167 region.addRect(clipIfValid(rightToLeft ? QRectF(fullLineRect.topLeft(), lineRect.bottomLeft())
1168 : QRectF(lineRect.topRight(), fullLineRect.bottomRight()), selectionClip));
1170 if (!selectionStartInLine) {
1171 region.addRect(clipIfValid(rightToLeft ? QRectF(lineRect.topRight(), fullLineRect.bottomRight())
1172 : QRectF(fullLineRect.topLeft(), lineRect.bottomLeft()), selectionClip));
1174 }
else if (!selectionEndInLine
1175 && isLastLineInBlock
1176 &&!(d->option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
1177 region.addRect(clipIfValid(QRectF(lineRect.right(), lineRect.top(),
1178 lineRect.height()/4, lineRect.height()), selectionClip));
1183 const QPen oldPen = p->pen();
1184 const QBrush oldBrush = p->brush();
1188 p->setClipRect(clip, Qt::IntersectClip);
1191 p->setPen(selection.format.penProperty(QTextFormat::OutlinePen));
1192 p->setBrush(selection.format.brushProperty(QTextFormat::BackgroundBrush));
1193 p->drawPath(region);
1196 p->setBrush(oldBrush);
1204 bool hasText = (selection.format.foreground().style() != Qt::NoBrush);
1205 bool hasBackground= (selection.format.background().style() != Qt::NoBrush);
1207 if (hasBackground) {
1208 selection.format.setProperty(
ObjectSelectionBrush, selection.format.property(QTextFormat::BackgroundBrush));
1211 selection.format.setProperty(QTextFormat::BackgroundBrush, QBrush());
1212 selection.format.clearProperty(QTextFormat::OutlinePen);
1217 if (hasText && !hasBackground && !(textDoneRegion & region).isEmpty())
1221 p->setClipPath(region, Qt::IntersectClip);
1223 for (
int line = firstLine; line < lastLine; ++line) {
1224 QTextLine l(line, d);
1225 l.draw_internal(p, position, &selection);
1230 textDoneRegion += region;
1233 textDoneRegion -= region;
1236 excludedRegion += region;
1239 QPainterPath needsTextButNoBackground = excludedRegion - textDoneRegion;
1240 if (!needsTextButNoBackground.isEmpty()){
1242 p->setClipPath(needsTextButNoBackground, Qt::IntersectClip);
1243 FormatRange selection;
1244 selection.start = 0;
1245 selection.length = INT_MAX;
1247 for (
int line = firstLine; line < lastLine; ++line) {
1248 QTextLine l(line, d);
1249 l.draw_internal(p, position, &selection);
1254 if (!excludedRegion.isEmpty()) {
1257 QRectF br = boundingRect().translated(position);
1258 br.setRight(QFIXED_MAX);
1260 br = br.intersected(clip);
1262 path -= excludedRegion;
1263 p->setClipPath(path, Qt::IntersectClip);
1266 for (
int i = firstLine; i < lastLine; ++i) {
1268 l.draw(p, position);
1270 if (!excludedRegion.isEmpty())
1274 if (!d->cacheGlyphs)
1279
1280
1281
1282
1283
1284
1285
1286void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition)
const
1288 drawCursor(p, pos, cursorPosition, 1);
1292
1293
1294
1295
1296
1297
1298void QTextLayout::drawCursor(QPainter *p,
const QPointF &pos,
int cursorPosition,
int width)
const
1300 if (d->lines.isEmpty())
1306 QPointF position = pos + d->position;
1308 cursorPosition = qBound(0, cursorPosition, d->layoutData->string.size());
1309 int line = d->lineNumberForTextPosition(cursorPosition);
1312 if (line >= d->lines.size())
1315 QTextLine l(line, d);
1316 const QScriptLine &sl = d->lines.at(line);
1318 qreal x = position.x() + l.cursorToX(cursorPosition);
1320 QFixed base = sl.base();
1321 QFixed descent = sl.descent;
1322 bool rightToLeft = d->isRightToLeft();
1324 const int realCursorPosition = cursorPosition;
1325 if (d->visualCursorMovement()) {
1326 if (cursorPosition == sl.from + sl.length)
1331 int itm = d->findItem(cursorPosition);
1334 const QScriptItem *si = &d->layoutData->items.at(itm);
1337 if (d->layoutData->hasBidi && !d->visualCursorMovement() && si->analysis.bidiLevel % 2 != rightToLeft) {
1338 int neighborItem = itm;
1339 if (neighborItem > 0 && si->position == realCursorPosition)
1341 else if (neighborItem < d->layoutData->items.size() - 1 && si->position + si->num_glyphs == realCursorPosition)
1343 const bool onBoundary = neighborItem != itm
1344 && si->analysis.bidiLevel != d->layoutData->items[neighborItem].analysis.bidiLevel;
1345 if (onBoundary && rightToLeft != si->analysis.bidiLevel % 2) {
1347 si = &d->layoutData->items[itm];
1351 if (si->analysis.flags != QScriptAnalysis::Object) {
1354 if (si->descent > 0)
1355 descent = si->descent;
1357 rightToLeft = si->analysis.bidiLevel % 2;
1359 qreal y = position.y() + (sl.y + sl.base() - base).toReal();
1360 bool toggleAntialiasing = !(p->renderHints() & QPainter::Antialiasing)
1361 && (p->transform().type() > QTransform::TxTranslate);
1362 if (toggleAntialiasing)
1363 p->setRenderHint(QPainter::Antialiasing);
1364 QPainter::CompositionMode origCompositionMode = p->compositionMode();
1370 QPaintDevice *targetDevice = p->paintEngine()->paintDevice();
1371 const bool targetHasAlphaChannel = targetDevice
1372 && targetDevice->devType() == QInternal::Image
1373 &&
static_cast<QImage *>(targetDevice)->hasAlphaChannel();
1374 if (!targetHasAlphaChannel && p->paintEngine()->hasFeature(QPaintEngine::RasterOpModes))
1375 p->setCompositionMode(QPainter::RasterOp_NotDestination);
1376 const QTransform &deviceTransform = p->deviceTransform();
1377 const qreal xScale = deviceTransform.m11();
1378 if (deviceTransform.type() != QTransform::TxScale || std::trunc(xScale) == xScale) {
1379 p->fillRect(QRectF(x, y, qreal(width), (base + descent).toReal()), p->pen().brush());
1380 }
else if (width > 0) {
1383 const QPen origPen = p->pen();
1384 QPen pen(origPen.brush(), qRound(width * xScale), Qt::SolidLine, Qt::FlatCap);
1385 pen.setCosmetic(
true);
1386 const qreal center = x + qreal(width) / 2;
1388 p->drawLine(QPointF(center, y), QPointF(center, qCeil(y + (base + descent).toReal())));
1391 p->setCompositionMode(origCompositionMode);
1392 if (toggleAntialiasing)
1393 p->setRenderHint(QPainter::Antialiasing,
false);
1394 if (d->layoutData->hasBidi) {
1395 const int arrow_extent = 4;
1396 int sign = rightToLeft ? -1 : 1;
1397 p->drawLine(QLineF(x, y, x + (sign * arrow_extent/2), y + arrow_extent/2));
1398 p->drawLine(QLineF(x, y+arrow_extent, x + (sign * arrow_extent/2), y + arrow_extent/2));
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1424
1425
1426
1427
1428
1431
1432
1433
1434
1435
1438
1439
1440
1441
1442
1443
1446
1447
1448
1449
1452
1453
1454
1455
1458
1459
1460
1461
1465
1466
1467
1468
1469QRectF QTextLine::rect()
const
1471 const QScriptLine& sl = eng->lines.at(index);
1472 return QRectF(sl.x.toReal(), sl.y.toReal(), sl.width.toReal(), sl.height().toReal());
1476
1477
1478QRectF QTextLine::naturalTextRect()
const
1480 const QScriptLine& sl = eng->lines.at(index);
1481 QFixed x = sl.x + eng->alignLine(sl);
1483 QFixed width = sl.textWidth;
1487 return QRectF(x.toReal(), sl.y.toReal(), width.toReal(), sl.height().toReal());
1491
1492
1493
1494
1495qreal QTextLine::x()
const
1497 return eng->lines.at(index).x.toReal();
1501
1502
1503
1504
1505qreal QTextLine::y()
const
1507 return eng->lines.at(index).y.toReal();
1511
1512
1513
1514
1515qreal QTextLine::width()
const
1517 return eng->lines.at(index).width.toReal();
1522
1523
1524
1525
1526qreal QTextLine::ascent()
const
1528 return eng->lines.at(index).ascent.toReal();
1532
1533
1534
1535
1536qreal QTextLine::descent()
const
1538 return eng->lines.at(index).descent.toReal();
1542
1543
1544
1545
1546
1547
1548qreal QTextLine::height()
const
1550 return eng->lines.at(index).height().ceil().toReal();
1554
1555
1556
1557
1558
1559
1560qreal QTextLine::leading()
const
1562 return eng->lines.at(index).leading.toReal();
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579void QTextLine::setLeadingIncluded(
bool included)
1581 eng->lines[index].leadingIncluded= included;
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595bool QTextLine::leadingIncluded()
const
1597 return eng->lines.at(index).leadingIncluded;
1601
1602
1603
1604
1605qreal QTextLine::naturalTextWidth()
const
1607 return eng->lines.at(index).textWidth.toReal();
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620qreal QTextLine::horizontalAdvance()
const
1622 return eng->lines.at(index).textAdvance.toReal();
1626
1627
1628
1629
1630
1631
1632void QTextLine::setLineWidth(qreal width)
1634 QScriptLine &line = eng->lines[index];
1635 if (!eng->layoutData) {
1636 qWarning(
"QTextLine: Can't set a line width while not layouting.");
1640 line.width = QFixed::fromReal(qBound(0.0, width, qreal(QFIXED_MAX)));
1642 && line.textWidth <= line.width
1643 && line.from + line.length == eng->layoutData->string.size())
1650 layout_helper(INT_MAX);
1654
1655
1656
1657
1658
1659
1660void QTextLine::setNumColumns(
int numColumns)
1662 QScriptLine &line = eng->lines[index];
1663 line.width = QFIXED_MAX;
1666 layout_helper(numColumns);
1670
1671
1672
1673
1674
1675
1676
1677void QTextLine::setNumColumns(
int numColumns, qreal alignmentWidth)
1679 QScriptLine &line = eng->lines[index];
1680 line.width = QFixed::fromReal(qBound(0.0, alignmentWidth, qreal(QFIXED_MAX)));
1683 layout_helper(numColumns);
1687#define LB_DEBUG qDebug
1689#define LB_DEBUG if (0
) qDebug
1694 struct LineBreakHelper
1696 LineBreakHelper() =
default;
1698 QScriptLine tmpData;
1699 QScriptLine spaceData;
1705 int currentPosition = 0;
1706 glyph_t previousGlyph = 0;
1707 QExplicitlySharedDataPointer<QFontEngine> previousGlyphFontEngine;
1710 QFixed currentSoftHyphenWidth;
1711 QFixed commitedSoftHyphenWidth;
1712 QFixed rightBearing;
1713 QFixed minimumRightBearing;
1715 QExplicitlySharedDataPointer<QFontEngine> fontEngine;
1716 const unsigned short *logClusters =
nullptr;
1718 bool manualWrap =
false;
1719 bool whiteSpaceOrObject =
true;
1721 bool checkFullOtherwiseExtend(QScriptLine &line);
1723 QFixed calculateNewWidth(
const QScriptLine &line)
const {
1724 return line.textWidth + tmpData.textWidth + spaceData.textWidth
1725 + (line.textWidth > 0 ? currentSoftHyphenWidth : QFixed()) + negativeRightBearing();
1728 inline glyph_t currentGlyph()
const
1730 Q_ASSERT(currentPosition > 0);
1731 Q_ASSERT(logClusters[currentPosition - 1] < glyphs.numGlyphs);
1733 return glyphs.glyphs[logClusters[currentPosition - 1]];
1736 inline void saveCurrentGlyph()
1739 if (currentPosition > 0 &&
1740 logClusters[currentPosition - 1] < glyphs.numGlyphs) {
1741 previousGlyph = currentGlyph();
1742 previousGlyphFontEngine = fontEngine;
1746 inline void calculateRightBearing(
QFontEngine *engine, glyph_t glyph)
1750 engine->getGlyphBearings(glyph,
nullptr, &rb);
1756 rightBearing = qMin(QFixed::fromReal(rb), QFixed(0));
1759 inline void calculateRightBearing()
1761 if (currentPosition <= 0)
1763 calculateRightBearing(fontEngine.data(), currentGlyph());
1766 inline void calculateRightBearingForPreviousGlyph()
1768 if (previousGlyph > 0)
1769 calculateRightBearing(previousGlyphFontEngine.data(), previousGlyph);
1772 static const QFixed RightBearingNotCalculated;
1774 inline void resetRightBearing()
1776 rightBearing = RightBearingNotCalculated;
1781 inline QFixed negativeRightBearing()
const
1783 if (rightBearing == RightBearingNotCalculated)
1786 return qAbs(rightBearing);
1790Q_CONSTINIT
const QFixed LineBreakHelper::RightBearingNotCalculated = QFixed(1);
1792inline bool LineBreakHelper::checkFullOtherwiseExtend(QScriptLine &line)
1794 LB_DEBUG(
"possible break width %f, spacew=%f", tmpData.textWidth.toReal(), spaceData.textWidth.toReal());
1796 QFixed newWidth = calculateNewWidth(line);
1797 if (line.length && !manualWrap && (newWidth > line.width || glyphCount > maxGlyphs))
1800 const QFixed oldTextWidth = line.textWidth;
1802 line.textWidth += spaceData.textWidth;
1804 line.length += spaceData.length;
1805 tmpData.textWidth = 0;
1807 spaceData.textWidth = 0;
1808 spaceData.length = 0;
1810 if (oldTextWidth != line.textWidth || currentSoftHyphenWidth > 0) {
1811 commitedSoftHyphenWidth = currentSoftHyphenWidth;
1812 currentSoftHyphenWidth = 0;
1821static inline void addNextCluster(
int &pos,
int end, QScriptLine &line,
int &glyphCount,
1822 const QScriptItem ¤t,
const unsigned short *logClusters,
1823 const QGlyphLayout &glyphs, QFixed *clusterWidth =
nullptr)
1825 int glyphPosition = logClusters[pos];
1829 }
while (pos < end && logClusters[pos] == glyphPosition);
1830 QFixed clusterWid = line.textWidth;
1833 line.textWidth += glyphs.advances[glyphPosition];
1837 Q_ASSERT((pos == end && glyphPosition == current
.num_glyphs) || logClusters[pos] == glyphPosition);
1840 *clusterWidth += (line.textWidth - clusterWid);
1846void QTextLine::layout_helper(
int maxGlyphs)
1848 QScriptLine &line = eng->lines[index];
1850 line.trailingSpaces = 0;
1852 line.hasTrailingSpaces =
false;
1854 if (!eng->layoutData->items.size() || line.from >= eng->layoutData->string.size()) {
1855 line.setDefaultHeight(eng);
1859 Q_ASSERT(line.from < eng->layoutData->string.size());
1861 LineBreakHelper lbh;
1863 lbh.maxGlyphs = maxGlyphs;
1865 QTextOption::WrapMode wrapMode = eng->option.wrapMode();
1866 bool breakany = (wrapMode == QTextOption::WrapAnywhere);
1867 const bool breakWordOrAny = breakany || (wrapMode == QTextOption::WrapAtWordBoundaryOrAnywhere);
1868 lbh.manualWrap = (wrapMode == QTextOption::ManualWrap || wrapMode == QTextOption::NoWrap);
1871 int newItem = eng->findItem(line.from);
1872 Q_ASSERT(newItem >= 0);
1874 LB_DEBUG(
"from: %d: item=%d, total %d, width available %f", line.from, newItem,
int(eng->layoutData->items.size()), line.width.toReal());
1876 Qt::Alignment alignment = eng->option.alignment();
1878 const QCharAttributes *attributes = eng->attributes();
1881 lbh.currentPosition = line.from;
1883 lbh.logClusters = eng->layoutData->logClustersPtr;
1884 lbh.previousGlyph = 0;
1886 bool manuallyWrapped =
false;
1887 bool hasInlineObject =
false;
1888 bool reachedEndOfLine =
false;
1889 QFixed maxInlineObjectHeight = 0;
1891 const bool includeTrailingSpaces = eng->option.flags() & QTextOption::IncludeTrailingSpaces;
1893 while (newItem < eng->layoutData->items.size()) {
1894 lbh.resetRightBearing();
1895 if (newItem != item) {
1897 const QScriptItem ¤t = eng->layoutData->items.at(item);
1898 if (!current.num_glyphs) {
1900 attributes = eng->attributes();
1903 lbh.logClusters = eng->layoutData->logClustersPtr;
1905 lbh.currentPosition = qMax(line.from, current.position);
1906 end = current.position + eng->length(item);
1907 lbh.glyphs = eng->shapedGlyphs(¤t);
1908 QFontEngine *fontEngine = eng->fontEngine(current);
1909 if (lbh.fontEngine != fontEngine) {
1910 lbh.fontEngine = fontEngine;
1911 lbh.minimumRightBearing = qMin(QFixed(),
1912 QFixed::fromReal(fontEngine->minRightBearing()));
1915 const QScriptItem ¤t = eng->layoutData->items.at(item);
1917 lbh.tmpData.leading = qMax(lbh.tmpData.leading + lbh.tmpData.ascent,
1918 current.leading + current.ascent) - qMax(lbh.tmpData.ascent,
1920 if (current.analysis.flags != QScriptAnalysis::Object
1921 || QTextDocumentPrivate::get(eng->block) ==
nullptr) {
1926 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1927 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1930 if (current.analysis.flags == QScriptAnalysis::Tab && (alignment & (Qt::AlignLeft | Qt::AlignRight | Qt::AlignCenter | Qt::AlignJustify))) {
1931 lbh.whiteSpaceOrObject =
true;
1932 if (lbh.checkFullOtherwiseExtend(line))
1935 QFixed x = line.x + line.textWidth + lbh.tmpData.textWidth + lbh.spaceData.textWidth;
1936 QFixed tabWidth = eng->calculateTabWidth(item, x);
1937 attributes = eng->attributes();
1940 lbh.logClusters = eng->layoutData->logClustersPtr;
1941 lbh.glyphs = eng->shapedGlyphs(¤t);
1943 lbh.spaceData.textWidth += tabWidth;
1944 lbh.spaceData.length++;
1947 QFixed averageCharWidth = eng->fontEngine(current)->averageCharWidth();
1948 lbh.glyphCount += qRound(tabWidth / averageCharWidth);
1950 if (lbh.checkFullOtherwiseExtend(line))
1952 }
else if (current.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator) {
1953 lbh.whiteSpaceOrObject =
true;
1956 if (!line.length && !lbh.tmpData.length)
1957 line.setDefaultHeight(eng);
1958 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators) {
1959 if (lbh.checkFullOtherwiseExtend(line))
1962 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
1963 current, lbh.logClusters, lbh.glyphs);
1965 lbh.tmpData.length++;
1966 lbh.calculateRightBearingForPreviousGlyph();
1968 line += lbh.tmpData;
1969 manuallyWrapped =
true;
1971 }
else if (current.analysis.flags == QScriptAnalysis::Object) {
1972 lbh.whiteSpaceOrObject =
true;
1973 lbh.tmpData.length++;
1975 if (QTextDocumentPrivate::get(eng->block) !=
nullptr) {
1976 QTextInlineObject inlineObject(item, eng);
1977 QTextFormat f = inlineObject.format();
1978 eng->docLayout()->positionInlineObject(inlineObject, eng->block.position() + current.position, f);
1979 QTextCharFormat::VerticalAlignment valign = f.toCharFormat().verticalAlignment();
1980 if (valign != QTextCharFormat::AlignTop && valign != QTextCharFormat::AlignBottom) {
1981 lbh.tmpData.ascent = qMax(lbh.tmpData.ascent, current.ascent);
1982 lbh.tmpData.descent = qMax(lbh.tmpData.descent, current.descent);
1986 lbh.tmpData.textWidth += current.width;
1990 if (lbh.checkFullOtherwiseExtend(line))
1993 hasInlineObject =
true;
1994 maxInlineObjectHeight = qMax(maxInlineObjectHeight, current.ascent + current.descent);
1996 }
else if (attributes[lbh.currentPosition].whiteSpace
1997 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
2000 if (lbh.currentPosition > 0 && !attributes[lbh.currentPosition - 1].whiteSpace)
2001 lbh.saveCurrentGlyph();
2002 lbh.whiteSpaceOrObject =
true;
2003 while (lbh.currentPosition < end
2004 && attributes[lbh.currentPosition].whiteSpace
2005 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak) {
2006 addNextCluster(lbh.currentPosition, end, lbh.spaceData, lbh.glyphCount,
2007 current, lbh.logClusters, lbh.glyphs);
2010 if (!lbh.manualWrap && lbh.spaceData.textWidth > line.width)
2013 lbh.whiteSpaceOrObject =
false;
2014 bool sb_or_ws =
false;
2018 if (lbh.currentPosition == 0
2019 || lbh.previousGlyph == 0
2020 || includeTrailingSpaces
2021 || !attributes[lbh.currentPosition - 1].whiteSpace) {
2022 lbh.saveCurrentGlyph();
2024 QFixed accumulatedTextWidth;
2026 addNextCluster(lbh.currentPosition, end, lbh.tmpData, lbh.glyphCount,
2027 current, lbh.logClusters, lbh.glyphs, &accumulatedTextWidth);
2034 const bool isBreakableSpace = lbh.currentPosition < eng->layoutData->string.size()
2035 && attributes[lbh.currentPosition].whiteSpace
2036 && eng->layoutData->string.at(lbh.currentPosition).decompositionTag() != QChar::NoBreak;
2038 if (lbh.currentPosition >= eng->layoutData->string.size()
2040 || attributes[lbh.currentPosition].lineBreak
2041 || lbh.tmpData.textWidth >= QFIXED_MAX) {
2044 }
else if (attributes[lbh.currentPosition].graphemeBoundary) {
2045 if (breakWordOrAny) {
2046 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2047 accumulatedTextWidth = 0;
2052 }
while (lbh.currentPosition < end);
2053 lbh.minw = qMax(accumulatedTextWidth, lbh.minw);
2055 if (lbh.currentPosition > 0 && lbh.currentPosition <= end
2056 && (lbh.currentPosition == end || attributes[lbh.currentPosition].lineBreak)
2057 && eng->layoutData->string.at(lbh.currentPosition - 1) == QChar::SoftHyphen) {
2074 lbh.currentSoftHyphenWidth = lbh.glyphs.advances[lbh.logClusters[lbh.currentPosition - 1]];
2077 if (sb_or_ws|breakany) {
2085 QFixed previousRightBearing = lbh.rightBearing;
2096 if ((lbh.calculateNewWidth(line) + qAbs(lbh.minimumRightBearing)) > line.width)
2097 lbh.calculateRightBearing();
2099 if (lbh.checkFullOtherwiseExtend(line)) {
2104 if (previousRightBearing != LineBreakHelper::RightBearingNotCalculated)
2105 lbh.rightBearing = previousRightBearing;
2107 lbh.calculateRightBearingForPreviousGlyph();
2109 line.textWidth += lbh.commitedSoftHyphenWidth;
2114 lbh.saveCurrentGlyph();
2116 if (lbh.currentPosition == end)
2120 reachedEndOfLine =
true;
2121 lbh.checkFullOtherwiseExtend(line);
2122 line.textWidth += lbh.commitedSoftHyphenWidth;
2124 line.textAdvance = line.textWidth;
2127 if (lbh.rightBearing == LineBreakHelper::RightBearingNotCalculated && !lbh.whiteSpaceOrObject)
2128 lbh.calculateRightBearing();
2131 const QFixed textWidthWithoutBearing = line.textWidth;
2132 line.textWidth += lbh.negativeRightBearing();
2134 if (line.length == 0) {
2135 LB_DEBUG(
"no break available in line, adding temp: length %d, width %f, space: length %d, width %f",
2136 lbh.tmpData.length, lbh.tmpData.textWidth.toReal(),
2137 lbh.spaceData.length, lbh.spaceData.textWidth.toReal());
2138 line += lbh.tmpData;
2141 if (hasInlineObject && QTextDocumentPrivate::get(eng->block) !=
nullptr) {
2143 if (maxInlineObjectHeight > line.ascent + line.descent) {
2145 QFixed toAdd = (maxInlineObjectHeight - line.ascent - line.descent)/2;
2146 line.ascent += toAdd;
2147 line.descent = maxInlineObjectHeight - line.ascent;
2149 int startItem = eng->findItem(line.from);
2150 int endItem = eng->findItem(line.from + line.length);
2152 endItem = eng->layoutData->items.size();
2153 for (
int item = startItem; item < endItem; ++item) {
2154 QScriptItem ¤t = eng->layoutData->items[item];
2155 if (current.analysis.flags == QScriptAnalysis::Object) {
2156 QTextInlineObject inlineObject(item, eng);
2157 QTextCharFormat::VerticalAlignment align = inlineObject.format().toCharFormat().verticalAlignment();
2158 QFixed height = current.ascent + current.descent;
2160 case QTextCharFormat::AlignTop:
2161 current.ascent = line.ascent;
2162 current.descent = height - line.ascent;
2164 case QTextCharFormat::AlignMiddle:
2165 current.ascent = (line.ascent + line.descent) / 2 - line.descent + height / 2;
2166 current.descent = height - line.ascent;
2168 case QTextCharFormat::AlignBottom:
2169 current.descent = line.descent;
2170 current.ascent = height - line.descent;
2175 Q_ASSERT(line.ascent >= current.ascent);
2176 Q_ASSERT(line.descent >= current.descent);
2182 LB_DEBUG(
"line length = %d, ascent=%f, descent=%f, textWidth=%f (spacew=%f)", line.length, line.ascent.toReal(),
2183 line.descent.toReal(), line.textWidth.toReal(), lbh.spaceData.width.toReal());
2184 LB_DEBUG(
" : '%s'", eng->layoutData->string.mid(line.from, line.length).toUtf8().data());
2186 const QFixed trailingSpace = (includeTrailingSpaces ? lbh.spaceData.textWidth : QFixed(0));
2187 if (eng->option.wrapMode() == QTextOption::WrapAtWordBoundaryOrAnywhere) {
2188 if ((lbh.maxGlyphs != INT_MAX && lbh.glyphCount > lbh.maxGlyphs)
2189 || (lbh.maxGlyphs == INT_MAX && line.textWidth > (line.width - trailingSpace))) {
2191 eng->option.setWrapMode(QTextOption::WrapAnywhere);
2192 layout_helper(lbh.maxGlyphs);
2193 eng->option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
2198 if (lbh.manualWrap) {
2199 eng->minWidth = qMax(eng->minWidth, line.textWidth);
2200 eng->maxWidth = qMax(eng->maxWidth, line.textWidth);
2202 eng->minWidth = qMax(eng->minWidth, lbh.minw);
2204 const QFixed actualTextWidth = manuallyWrapped || reachedEndOfLine
2206 : textWidthWithoutBearing;
2207 if (qAddOverflow(eng->layoutData->currentMaxWidth, actualTextWidth, &eng->layoutData->currentMaxWidth))
2208 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2209 if (!manuallyWrapped) {
2210 if (qAddOverflow(eng->layoutData->currentMaxWidth, lbh.spaceData.textWidth, &eng->layoutData->currentMaxWidth))
2211 eng->layoutData->currentMaxWidth = QFIXED_MAX;
2213 eng->maxWidth = qMax(eng->maxWidth, eng->layoutData->currentMaxWidth);
2214 if (manuallyWrapped)
2215 eng->layoutData->currentMaxWidth = 0;
2218 line.textWidth += trailingSpace;
2219 if (lbh.spaceData.length) {
2220 line.trailingSpaces = lbh.spaceData.length;
2221 line.hasTrailingSpaces =
true;
2224 line.justified =
false;
2225 line.gridfitted =
false;
2229
2230
2231void QTextLine::setPosition(
const QPointF &pos)
2233 eng->lines[index].x = QFixed::fromReal(pos.x());
2234 eng->lines[index].y = QFixed::fromReal(pos.y());
2238
2239
2240QPointF QTextLine::position()
const
2242 return QPointF(eng->lines.at(index).x.toReal(), eng->lines.at(index).y.toReal());
2253
2254
2255
2256int QTextLine::textStart()
const
2258 return eng->lines.at(index).from;
2262
2263
2264
2265
2266int QTextLine::textLength()
const
2268 if (eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators
2269 && eng->block.isValid() && index == eng->lines.size()-1) {
2270 return eng->lines.at(index).length - 1;
2272 return eng->lines.at(index).length + eng->lines.at(index).trailingSpaces;
2277 QBrush bg = chf.background();
2279 p->fillRect(r.toAlignedRect(), bg);
2284 QBrush c = chf.foreground();
2285 if (c.style() == Qt::NoBrush)
2286 p->setPen(defaultPen);
2288 p->setPen(QPen(c, 0));
2291#if !defined(QT_NO_RAWFONT)
2293 const QString &text,
2296 const QGlyphRun::GlyphRunFlags &flags,
2297 QTextLayout::GlyphRunRetrievalFlags retrievalFlags,
2299 QFixed selectionWidth,
2302 unsigned short *logClusters,
2306 Q_ASSERT(logClusters !=
nullptr);
2312 int rangeStart = textPosition;
2313 int logClusterIndex = 0;
2314 while (logClusters[logClusterIndex] != glyphsStart && rangeStart < textPosition + textLength) {
2319 int rangeEnd = rangeStart;
2320 while (logClusters[logClusterIndex] != glyphsEnd && rangeEnd < textPosition + textLength) {
2330 QRawFontPrivate *fontD = QRawFontPrivate::get(font);
2331 fontD->setFontEngine(fontEngine);
2333 QVarLengthArray<glyph_t> glyphsArray;
2334 QVarLengthArray<QFixedPoint> positionsArray;
2336 QTextItem::RenderFlags renderFlags;
2337 if (flags.testFlag(QGlyphRun::Overline))
2338 renderFlags |= QTextItem::Overline;
2339 if (flags.testFlag(QGlyphRun::Underline))
2340 renderFlags |= QTextItem::Underline;
2341 if (flags.testFlag(QGlyphRun::StrikeOut))
2342 renderFlags |= QTextItem::StrikeOut;
2343 if (flags.testFlag(QGlyphRun::RightToLeft))
2344 renderFlags |= QTextItem::RightToLeft;
2346 fontEngine->getGlyphPositions(glyphLayout, QTransform(), renderFlags, glyphsArray,
2348 Q_ASSERT(glyphsArray.size() == positionsArray.size());
2350 qreal fontHeight = font.ascent() + font.descent();
2353 QList<quint32> glyphs;
2354 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2355 glyphs.reserve(glyphsArray.size());
2356 QList<QPointF> positions;
2357 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2358 positions.reserve(glyphsArray.size());
2359 QList<qsizetype> stringIndexes;
2360 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2361 stringIndexes.reserve(glyphsArray.size());
2363 int nextClusterIndex = 0;
2364 int currentClusterIndex = 0;
2365 for (
int i = 0; i < glyphsArray.size(); ++i) {
2366 const int glyphArrayIndex = i + glyphsStart;
2369 if (retrievalFlags & QTextLayout::RetrieveStringIndexes) {
2370 if (nextClusterIndex < textLength && logClusters[nextClusterIndex] == glyphArrayIndex) {
2371 currentClusterIndex = nextClusterIndex;
2372 while (logClusters[nextClusterIndex] == glyphArrayIndex && nextClusterIndex < textLength)
2379 Q_ASSERT(nextClusterIndex == textLength || logClusters[nextClusterIndex] != glyphArrayIndex);
2380 stringIndexes.append(textPosition + currentClusterIndex);
2383 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes) {
2384 glyph_t glyphIndex = glyphsArray.at(i) & 0xffffff;
2385 glyphs.append(glyphIndex);
2388 QPointF position = positionsArray.at(i).toPointF() + pos;
2389 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2390 positions.append(position);
2393 maxY = minY = position.y();
2395 minY = qMin(minY, position.y());
2396 maxY = qMax(maxY, position.y());
2400 qreal height = maxY + fontHeight - minY;
2402 if (retrievalFlags & QTextLayout::RetrieveGlyphIndexes)
2403 glyphRun.setGlyphIndexes(glyphs);
2404 if (retrievalFlags & QTextLayout::RetrieveGlyphPositions)
2405 glyphRun.setPositions(positions);
2406 if (retrievalFlags & QTextLayout::RetrieveStringIndexes)
2407 glyphRun.setStringIndexes(stringIndexes);
2408 if (retrievalFlags & QTextLayout::RetrieveString)
2409 glyphRun.setSourceString(text);
2410 glyphRun.setFlags(flags);
2411 glyphRun.setRawFont(font);
2413 glyphRun.setBoundingRect(QRectF(selectionX.toReal(), minY - font.ascent(),
2414 selectionWidth.toReal(), height));
2419# if QT_VERSION < QT_VERSION_CHECK(7
, 0
, 0
)
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440QList<QGlyphRun> QTextLine::glyphRuns(
int from,
int length)
const
2442 return glyphRuns(from, length, QTextLayout::GlyphRunRetrievalFlag::DefaultRetrievalFlags);
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463QList<QGlyphRun> QTextLine::glyphRuns(
int from,
2465 QTextLayout::GlyphRunRetrievalFlags retrievalFlags)
const
2467 const QScriptLine &line = eng->lines.at(index);
2469 if (line.length == 0)
2470 return QList<QGlyphRun>();
2476 length = textLength();
2479 return QList<QGlyphRun>();
2481 QTextLayout::FormatRange selection;
2482 selection.start = from;
2483 selection.length = length;
2485 QTextLineItemIterator iterator(eng, index, QPointF(), &selection);
2486 qreal y = line.y.toReal() + line.base().toReal();
2487 QList<QGlyphRun> glyphRuns;
2488 while (!iterator.atEnd()) {
2489 QScriptItem &si = iterator.next();
2490 if (si.analysis.flags >= QScriptAnalysis::TabOrObject)
2493 if (from >= 0 && length >= 0 && (from >= iterator.itemEnd || from + length <= iterator.itemStart))
2496 QPointF pos(iterator.x.toReal(), y);
2499 QGlyphRun::GlyphRunFlags flags;
2500 if (!eng->useRawFont) {
2501 font = eng->font(si);
2502 if (font.overline())
2503 flags |= QGlyphRun::Overline;
2504 if (font.underline())
2505 flags |= QGlyphRun::Underline;
2506 if (font.strikeOut())
2507 flags |= QGlyphRun::StrikeOut;
2511 if (si.analysis.bidiLevel % 2) {
2512 flags |= QGlyphRun::RightToLeft;
2516 int relativeFrom = qMax(iterator.itemStart, from) - si.position;
2517 int relativeTo = qMin(iterator.itemEnd, from + length) - 1 - si.position;
2519 unsigned short *logClusters = eng->logClusters(&si);
2520 int glyphsStart = logClusters[relativeFrom];
2521 int glyphsEnd = (relativeTo == iterator.itemLength) ? si.num_glyphs - 1 : logClusters[relativeTo];
2523 int nextGlyphIndex = (relativeTo < iterator.itemLength - 1) ? logClusters[relativeTo + 1] : si.num_glyphs;
2524 if (nextGlyphIndex - 1 > glyphsEnd)
2525 glyphsEnd = nextGlyphIndex - 1;
2526 bool startsInsideLigature = relativeFrom > 0 && logClusters[relativeFrom - 1] == glyphsStart;
2527 bool endsInsideLigature = nextGlyphIndex == glyphsEnd;
2529 int itemGlyphsStart = logClusters[iterator.itemStart - si.position];
2530 int itemGlyphsEnd = logClusters[iterator.itemEnd - 1 - si.position];
2532 QGlyphLayout glyphLayout = eng->shapedGlyphs(&si);
2537 if (relativeFrom != (iterator.itemStart - si.position) && !rtl) {
2538 for (
int i = itemGlyphsStart; i < glyphsStart; ++i) {
2539 if (!glyphLayout.attributes[i].dontPrint) {
2540 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2541 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2544 }
else if (relativeTo != (iterator.itemEnd - si.position - 1) && rtl) {
2545 for (
int i = itemGlyphsEnd; i > glyphsEnd; --i) {
2546 if (!glyphLayout.attributes[i].dontPrint) {
2547 QFixed justification = QFixed::fromFixed(glyphLayout.justifications[i].space_18d6);
2548 pos.rx() += (glyphLayout.advances[i] + justification).toReal();
2553 glyphLayout = glyphLayout.mid(glyphsStart, glyphsEnd - glyphsStart + 1);
2557 iterator.getSelectionBounds(&x, &width);
2559 if (glyphLayout.numGlyphs > 0) {
2560 QFontEngine *mainFontEngine;
2561#ifndef QT_NO_RAWFONT
2562 if (eng->useRawFont && eng->rawFont.isValid())
2563 mainFontEngine= eng->fontEngine(si);
2566 mainFontEngine = font.d->engineForScript(si.analysis.script);
2568 if (mainFontEngine->type() == QFontEngine::Multi) {
2569 QFontEngineMulti *multiFontEngine =
static_cast<QFontEngineMulti *>(mainFontEngine);
2570 int start = rtl ? glyphLayout.numGlyphs : 0;
2571 int end = start - 1;
2572 int which = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2573 for (; (rtl && start > 0) || (!rtl && end < glyphLayout.numGlyphs - 1);
2574 rtl ? --start : ++end) {
2575 const int e = glyphLayout.glyphs[rtl ? start - 1 : end + 1] >> 24;
2579 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2580 multiFontEngine->ensureEngineAt(which);
2582 QGlyphRun::GlyphRunFlags subFlags = flags;
2583 if (start == 0 && startsInsideLigature)
2584 subFlags |= QGlyphRun::SplitLigature;
2587 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2595 glyphsStart + start,
2597 logClusters + relativeFrom,
2598 relativeFrom + si.position,
2599 relativeTo - relativeFrom + 1);
2600 if (!glyphRun.isEmpty())
2601 glyphRuns.append(glyphRun);
2603 for (
int i = 0; i < subLayout.numGlyphs; ++i) {
2604 if (!subLayout.attributes[i].dontPrint) {
2605 QFixed justification = QFixed::fromFixed(subLayout.justifications[i].space_18d6);
2606 pos.rx() += (subLayout.advances[i] + justification).toReal();
2617 QGlyphLayout subLayout = glyphLayout.mid(start, end - start + 1);
2618 multiFontEngine->ensureEngineAt(which);
2620 QGlyphRun::GlyphRunFlags subFlags = flags;
2621 if ((start == 0 && startsInsideLigature) || endsInsideLigature)
2622 subFlags |= QGlyphRun::SplitLigature;
2624 QGlyphRun glyphRun = glyphRunWithInfo(multiFontEngine->engine(which),
2632 glyphsStart + start,
2634 logClusters + relativeFrom,
2635 relativeFrom + si.position,
2636 relativeTo - relativeFrom + 1);
2637 if (!glyphRun.isEmpty())
2638 glyphRuns.append(glyphRun);
2640 if (startsInsideLigature || endsInsideLigature)
2641 flags |= QGlyphRun::SplitLigature;
2642 QGlyphRun glyphRun = glyphRunWithInfo(mainFontEngine,
2652 logClusters + relativeFrom,
2653 relativeFrom + si.position,
2654 relativeTo - relativeFrom + 1);
2655 if (!glyphRun.isEmpty())
2656 glyphRuns.append(glyphRun);
2666
2667
2668
2669
2670void QTextLine::draw(QPainter *painter,
const QPointF &position)
const
2672 draw_internal(painter, position,
nullptr);
2675void QTextLine::draw_internal(QPainter *p,
const QPointF &origPos,
2676 const QTextLayout::FormatRange *selection)
const
2678#ifndef QT_NO_RAWFONT
2680 Q_ASSERT(!eng->useRawFont);
2682 const QScriptLine &line = eng->lines[index];
2684 bool noText = (selection && selection->format.property(
SuppressText).toBool());
2688 && selection->start <= line.from
2689 && selection->start + selection->length > line.from) {
2691 const qreal lineHeight = line.height().toReal();
2692 QRectF r(origPos.x() + line.x.toReal(), origPos.y() + line.y.toReal(),
2693 lineHeight / 2, QFontMetrics(eng->font()).horizontalAdvance(u' '));
2694 drawBackground(p, selection->format, r);
2699 Q_CONSTINIT
static QRectF maxFixedRect(-QFIXED_MAX / 2, -QFIXED_MAX / 2, QFIXED_MAX, QFIXED_MAX);
2700 const bool xlateToFixedRange = !maxFixedRect.contains(origPos);
2702 if (Q_LIKELY(!xlateToFixedRange))
2705 p->translate(origPos);
2708 QFixed lineBase = line.base();
2709 eng->clearDecorations();
2710 eng->enableDelayDecorations();
2712 const QFixed y = QFixed::fromReal(pos.y()) + line.y + lineBase;
2714 const QTextFormatCollection *formatCollection = eng->formatCollection();
2716 bool suppressColors = (eng->option.flags() & QTextOption::SuppressColors);
2718 auto prepareFormat = [suppressColors, selection,
this](QTextCharFormat &format,
2720 format.merge(eng->format(si));
2722 if (suppressColors) {
2723 format.clearForeground();
2724 format.clearBackground();
2725 format.clearProperty(QTextFormat::TextUnderlineColor);
2728 format.merge(selection->format);
2732 QTextLineItemIterator iterator(eng, index, pos, selection);
2733 while (!iterator.atEnd()) {
2734 QScriptItem &si = iterator.next();
2736 if (eng->hasFormats() || selection || formatCollection) {
2737 QTextCharFormat format;
2738 if (formatCollection !=
nullptr)
2739 format = formatCollection->defaultTextFormat();
2740 prepareFormat(format, &si);
2741 drawBackground(p, format, QRectF(iterator.x.toReal(), (y - lineBase).toReal(),
2742 iterator.itemWidth.toReal(), line.height().toReal()));
2747 QPen pen = p->pen();
2749 QTextLineItemIterator iterator(eng, index, pos, selection);
2750 while (!iterator.atEnd()) {
2751 QScriptItem &si = iterator.next();
2753 if (selection && selection->start >= 0 && iterator.isOutsideSelection())
2756 if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
2757 && !(eng->option.flags() & QTextOption::ShowLineAndParagraphSeparators))
2760 QFixed itemBaseLine = y;
2761 QFont f = eng->font(si);
2762 QTextCharFormat format;
2763 if (formatCollection !=
nullptr)
2764 format = formatCollection->defaultTextFormat();
2766 if (eng->hasFormats() || selection || formatCollection) {
2767 prepareFormat(format, &si);
2768 setPen(p, pen, format);
2770 const qreal baseLineOffset = format.baselineOffset() / 100.0;
2771 QTextCharFormat::VerticalAlignment valign = format.verticalAlignment();
2772 if (valign == QTextCharFormat::AlignSuperScript
2773 || valign == QTextCharFormat::AlignSubScript
2774 || !qFuzzyIsNull(baseLineOffset))
2776 QFontEngine *fe = f.d->engineForScript(si.analysis.script);
2777 QFixed height = fe->ascent() + fe->descent();
2778 itemBaseLine -= height * QFixed::fromReal(baseLineOffset);
2780 if (valign == QTextCharFormat::AlignSubScript)
2781 itemBaseLine += height * QFixed::fromReal(format.subScriptBaseline() / 100.0);
2782 else if (valign == QTextCharFormat::AlignSuperScript)
2783 itemBaseLine -= height * QFixed::fromReal(format.superScriptBaseline() / 100.0);
2787 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
2789 if (eng->hasFormats()) {
2791 if (si.analysis.flags == QScriptAnalysis::Object && QTextDocumentPrivate::get(eng->block)) {
2792 QFixed itemY = y - si.ascent;
2793 switch (format.verticalAlignment()) {
2794 case QTextCharFormat::AlignTop:
2795 itemY = y - lineBase;
2797 case QTextCharFormat::AlignMiddle:
2798 itemY = y - lineBase + (line.height() - si.height()) / 2;
2800 case QTextCharFormat::AlignBottom:
2801 itemY = y - lineBase + line.height() - si.height();
2807 QRectF itemRect(iterator.x.toReal(), itemY.toReal(), iterator.itemWidth.toReal(), si.height().toReal());
2809 eng->docLayout()->drawInlineObject(p, itemRect,
2810 QTextInlineObject(iterator.item, eng),
2811 si.position + eng->block.position(),
2815 if (bg.style() != Qt::NoBrush) {
2816 QColor c = bg.color();
2818 p->fillRect(itemRect, c);
2822 QFont f = eng->font(si);
2823 QTextItemInt gf(si, &f, format);
2826 gf.width = iterator.itemWidth;
2827 QPainterPrivate::get(p)->drawTextItem(QPointF(iterator.x.toReal(), y.toReal()), gf, eng);
2828 if (eng->option.flags() & QTextOption::ShowTabsAndSpaces) {
2829 const QChar visualTab = QChar(QChar::VisualTabCharacter);
2830 int w = QFontMetrics(f).horizontalAdvance(visualTab);
2831 qreal x = iterator.itemWidth.toReal() - w;
2833 p->setClipRect(QRectF(iterator.x.toReal(), line.y.toReal(),
2834 iterator.itemWidth.toReal(), line.height().toReal()),
2839 p->drawText(QPointF(iterator.x.toReal() + x,
2840 y.toReal()), visualTab);
2850 unsigned short *logClusters = eng->logClusters(&si);
2851 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
2853 QTextItemInt gf(glyphs.mid(iterator.glyphsStart, iterator.glyphsEnd - iterator.glyphsStart),
2854 &f, eng->layoutData->string.unicode() + iterator.itemStart,
2855 iterator.itemEnd - iterator.itemStart, eng->fontEngine(si), format);
2856 gf.logClusters = logClusters + iterator.itemStart - si.position;
2857 gf.width = iterator.itemWidth;
2858 gf.justified = line.justified;
2859 gf.initWithScriptItem(si);
2861 Q_ASSERT(gf.fontEngine);
2863 QPointF pos(iterator.x.toReal(), itemBaseLine.toReal());
2864 if (format.penProperty(QTextFormat::TextOutline).style() != Qt::NoPen) {
2866 path.setFillRule(Qt::WindingFill);
2868 if (gf.glyphs.numGlyphs)
2869 gf.fontEngine->addOutlineToPath(pos.x(), pos.y(), gf.glyphs, &path, gf.flags);
2871 const QFontEngine *fe = gf.fontEngine;
2872 const qreal lw = fe->lineThickness().toReal();
2873 if (gf.flags & QTextItem::Underline) {
2874 qreal offs = fe->underlinePosition().toReal();
2875 path.addRect(pos.x(), pos.y() + offs, gf.width.toReal(), lw);
2877 if (gf.flags & QTextItem::Overline) {
2878 qreal offs = fe->ascent().toReal() + 1;
2879 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2881 if (gf.flags & QTextItem::StrikeOut) {
2882 qreal offs = fe->ascent().toReal() / 3;
2883 path.addRect(pos.x(), pos.y() - offs, gf.width.toReal(), lw);
2888 p->setRenderHint(QPainter::Antialiasing);
2891 if (p->pen().style() == Qt::NoPen)
2892 p->setBrush(Qt::NoBrush);
2894 p->setBrush(p->pen().brush());
2896 p->setPen(format.textOutline());
2901 gf.glyphs.numGlyphs = 0;
2902 QPainterPrivate::get(p)->drawTextItem(pos, gf, eng);
2905 if ((si.analysis.flags == QScriptAnalysis::Space
2906 || si.analysis.flags == QScriptAnalysis::Nbsp)
2907 && (eng->option.flags() & QTextOption::ShowTabsAndSpaces)) {
2908 QBrush c = format.foreground();
2909 if (c.style() != Qt::NoBrush)
2910 p->setPen(c.color());
2911 const QChar visualSpace = si.analysis.flags == QScriptAnalysis::Space ? u'\xb7' : u'\xb0';
2912 QFont oldFont = p->font();
2913 p->setFont(eng->font(si));
2914 p->drawText(QPointF(iterator.x.toReal(), itemBaseLine.toReal()), visualSpace);
2916 p->setFont(oldFont);
2920 eng->drawDecorations(p);
2922 if (xlateToFixedRange)
2923 p->translate(-origPos);
2925 if (eng->hasFormats())
2930
2931
2932
2933
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945qreal QTextLine::cursorToX(
int *cursorPos, Edge edge)
const
2947 const QScriptLine &line = eng->lines[index];
2948 bool lastLine = index >= eng->lines.size() - 1;
2950 QFixed x = line.x + eng->alignLine(line) - eng->leadingSpaceWidth(line);
2952 if (!eng->layoutData)
2954 if (!eng->layoutData->items.size()) {
2955 *cursorPos = line.from;
2959 int lineEnd = line.from + line.length + line.trailingSpaces;
2960 int pos = qBound(line.from, *cursorPos, lineEnd);
2961 const QCharAttributes *attributes = eng->attributes();
2963 *cursorPos = line.from;
2966 while (pos < lineEnd && !attributes[pos].graphemeBoundary)
2969 int itm = pos == lineEnd ? eng->findItem(pos-1) : eng->findItem(pos);
2971 *cursorPos = line.from;
2974 eng->shapeLine(line);
2976 const QScriptItem *scriptItem = &eng->layoutData->items[itm];
2977 if (!scriptItem->num_glyphs)
2980 if ((scriptItem->analysis.bidiLevel % 2 != eng->isRightToLeft()) && !eng->visualCursorMovement()) {
2983 int neighborItem = itm;
2984 if (neighborItem > 0 && scriptItem->position == pos)
2986 else if (neighborItem < eng->layoutData->items.size() - 1 && scriptItem->position + scriptItem->num_glyphs == pos)
2988 const bool onBoundary = neighborItem != itm && scriptItem->analysis.bidiLevel != eng->layoutData->items[neighborItem].analysis.bidiLevel;
2991 if (eng->isRightToLeft() != scriptItem->analysis.bidiLevel % 2) {
2993 scriptItem = &eng->layoutData->items[itm];
2994 if (!scriptItem->num_glyphs)
3000 const int l = eng->length(itm);
3001 pos = qBound(0, pos - scriptItem->position, l);
3003 QGlyphLayout glyphs = eng->shapedGlyphs(scriptItem);
3004 unsigned short *logClusters = eng->logClusters(scriptItem);
3005 Q_ASSERT(logClusters);
3007 int glyph_pos = pos == l ? scriptItem->num_glyphs : logClusters[pos];
3008 if (edge == Trailing && glyph_pos < scriptItem->num_glyphs) {
3011 while (glyph_pos < scriptItem->num_glyphs && !glyphs.attributes[glyph_pos].clusterStart)
3015 bool reverse = scriptItem->analysis.bidiLevel % 2;
3020 int firstItem = eng->findItem(line.from);
3021 int lastItem = eng->findItem(lineEnd - 1, itm);
3022 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3024 QVarLengthArray<
int> visualOrder(nItems);
3025 QVarLengthArray<uchar> levels(nItems);
3026 for (
int i = 0; i < nItems; ++i)
3027 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3028 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3030 for (
int i = 0; i < nItems; ++i) {
3031 int item = visualOrder[i]+firstItem;
3034 QScriptItem &si = eng->layoutData->items[item];
3038 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3043 const int itemLength = eng->length(item);
3044 int start = qMax(line.from, si.position);
3045 int end = qMin(lineEnd, si.position + itemLength);
3047 logClusters = eng->logClusters(&si);
3049 int gs = logClusters[start-si.position];
3050 int ge = (end == si.position + itemLength) ? si.num_glyphs-1 : logClusters[end-si.position-1];
3052 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3055 x += glyphs.effectiveAdvance(gs);
3060 logClusters = eng->logClusters(scriptItem);
3061 glyphs = eng->shapedGlyphs(scriptItem);
3062 if (scriptItem->analysis.flags >= QScriptAnalysis::TabOrObject) {
3063 if (pos == (reverse ? 0 : l))
3064 x += scriptItem->width;
3066 bool rtl = eng->isRightToLeft();
3067 bool visual = eng->visualCursorMovement();
3068 int end = qMin(lineEnd, scriptItem->position + l) - scriptItem->position;
3070 int glyph_end = end == l ? scriptItem->num_glyphs : logClusters[end];
3071 int glyph_start = glyph_pos;
3072 if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
3074 for (
int i = glyph_end - 1; i >= glyph_start; i--)
3075 x += glyphs.effectiveAdvance(i);
3076 x -= eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3078 int start = qMax(line.from - scriptItem->position, 0);
3079 int glyph_start = logClusters[start];
3080 int glyph_end = glyph_pos;
3081 if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
3083 for (
int i = glyph_start; i <= glyph_end; i++)
3084 x += glyphs.effectiveAdvance(i);
3085 x += eng->offsetInLigature(scriptItem, pos, end, glyph_pos);
3089 if (eng->option.wrapMode() != QTextOption::NoWrap && x > line.x + line.width)
3090 x = line.x + line.width;
3091 if (eng->option.wrapMode() != QTextOption::NoWrap && x < 0)
3094 *cursorPos = pos + scriptItem->position;
3099
3100
3101
3102
3103
3104
3105
3106
3107int QTextLine::xToCursor(qreal _x, CursorPosition cpos)
const
3109 QFixed x = QFixed::fromReal(_x);
3110 const QScriptLine &line = eng->lines[index];
3111 bool lastLine = index >= eng->lines.size() - 1;
3112 int lineNum = index;
3114 if (!eng->layoutData)
3117 int line_length = textLength();
3122 int firstItem = eng->findItem(line.from);
3123 int lastItem = eng->findItem(line.from + line_length - 1, firstItem);
3124 int nItems = (firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0;
3130 x -= eng->alignLine(line);
3133 QVarLengthArray<
int> visualOrder(nItems);
3134 QVarLengthArray<
unsigned char> levels(nItems);
3135 for (
int i = 0; i < nItems; ++i)
3136 levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
3137 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
3139 bool visual = eng->visualCursorMovement();
3142 if (eng->isRightToLeft())
3143 return line.from + line_length;
3145 }
else if (x < line.textWidth || (line.justified && x < line.width)) {
3148 bool rtl = eng->isRightToLeft();
3150 eng->shapeLine(line);
3151 const auto insertionPoints = (visual && rtl) ? eng->insertionPointsForLine(lineNum) : std::vector<
int>();
3153 for (
int i = 0; i < nItems; ++i) {
3154 int item = visualOrder[i]+firstItem;
3155 QScriptItem &si = eng->layoutData->items[item];
3156 int item_length = eng->length(item);
3159 int start = qMax(line.from - si.position, 0);
3160 int end = qMin(line.from + line_length - si.position, item_length);
3162 unsigned short *logClusters = eng->logClusters(&si);
3164 int gs = logClusters[start];
3165 int ge = (end == item_length ? si.num_glyphs : logClusters[end]) - 1;
3166 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
3168 QFixed item_width = 0;
3169 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3170 item_width = si.width;
3174 item_width += glyphs.effectiveAdvance(g);
3180 if (pos + item_width < x) {
3186 if (si.analysis.flags >= QScriptAnalysis::TabOrObject) {
3187 if (cpos == QTextLine::CursorOnCharacter)
3189 bool left_half = (x - pos) < item_width/2;
3191 if (
bool(si.analysis.bidiLevel % 2) != left_half)
3193 return si.position + 1;
3199 if (cpos == QTextLine::CursorOnCharacter) {
3200 if (si.analysis.bidiLevel % 2) {
3204 if (glyphs.attributes[gs].clusterStart) {
3210 pos -= glyphs.effectiveAdvance(gs);
3216 if (glyphs.attributes[gs].clusterStart) {
3222 pos += glyphs.effectiveAdvance(gs);
3227 QFixed dist = INT_MAX/256;
3228 if (si.analysis.bidiLevel % 2) {
3229 if (!visual || rtl || (lastLine && i == nItems - 1)) {
3232 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3237 pos -= glyphs.effectiveAdvance(gs);
3242 if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
3247 pos += glyphs.effectiveAdvance(ge);
3252 if (!visual || !rtl || (lastLine && i == 0)) {
3254 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3259 pos += glyphs.effectiveAdvance(gs);
3263 QFixed oldPos = pos;
3265 pos += glyphs.effectiveAdvance(gs);
3266 if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
3276 if (qAbs(x-pos) < dist) {
3278 if (!rtl && i < nItems - 1) {
3282 if (rtl && nchars > 0)
3283 return insertionPoints[size_t(lastLine ? nchars : nchars - 1)];
3285 return eng->positionInLigature(&si, end, x, pos, -1,
3286 cpos == QTextLine::CursorOnCharacter);
3289 Q_ASSERT(glyph_pos != -1);
3290 return eng->positionInLigature(&si, end, x, edge, glyph_pos,
3291 cpos == QTextLine::CursorOnCharacter);
3295 int pos = line.from;
3296 if (!eng->isRightToLeft())
3303 if (index < eng->lines.size() - 1)
3304 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