@@ -178,7 +178,10 @@ int EditorLayout::hitTest(const QPointF& point, Qt::HitTestAccuracy accuracy) co
178
178
int lineCount = layout->lineCount ();
179
179
for (int i = 0 ; i < lineCount; i++) {
180
180
QTextLine line = layout->lineAt (i);
181
- if (!line.rect ().contains (blockPoint) && i < lineCount - 1 ) {
181
+
182
+ QRectF lineRect = line.rect ();
183
+ bool inLine = blockPoint.y () >= lineRect.top () && blockPoint.y () <= lineRect.bottom ();
184
+ if (!inLine && i < lineCount - 1 ) {
182
185
continue ;
183
186
}
184
187
@@ -333,10 +336,9 @@ void EditorLayout::documentChanged(int position, int charsRemoved, int charsAdde
333
336
Q_EMIT update ();
334
337
}
335
338
336
- static void buildDisplayLayout (const QTextBlock& block, LayoutBlockData* blockData)
339
+ static void buildDisplayLayout (const QTextBlock& block, QString blockText, LayoutBlockData* blockData)
337
340
{
338
- QString text = block.text ();
339
- size_t newHash = qHash (text);
341
+ size_t newHash = qHash (blockText);
340
342
341
343
// Calculating a display layout is expensive, only do it if content actually changed
342
344
if (newHash == blockData->textHash ) {
@@ -355,14 +357,16 @@ static void buildDisplayLayout(const QTextBlock& block, LayoutBlockData* blockDa
355
357
QList<QTextLayout::FormatRange> formats = defaultLayout->formats ();
356
358
parsing::IsolateRangeList isolates = isolateData->isolates ();
357
359
360
+ blockText.detach ();
361
+
358
362
// Inject Unicode BiDi control characters the block's text for the needed
359
363
// isolate ranges, creating a different text that is used for shaping but
360
364
// not edit purposes. This moves around positions of visible characters
361
365
// relative to the editable block content stored in the QTextDocument, so
362
366
// we need to save an offset mapping.
363
367
364
368
auto & offsets = blockData->displayOffsets ;
365
- offsets.resize (text .size ());
369
+ offsets.resize (blockText .size ());
366
370
offsets.fill (0 );
367
371
368
372
QMap<int , int > isolatesStartingAt;
@@ -379,8 +383,8 @@ static void buildDisplayLayout(const QTextBlock& block, LayoutBlockData* blockDa
379
383
int start = static_cast <int >(isolate.startPos );
380
384
int end = static_cast <int >(isolate.endPos );
381
385
382
- text .insert (start + offsets[start], startChar);
383
- text .insert (end + offsets[end] + 2 , utils::PDI_MARK);
386
+ blockText .insert (start + offsets[start], startChar);
387
+ blockText .insert (end + offsets[end] + 2 , utils::PDI_MARK);
384
388
385
389
for (int i = start; i <= end; i++) {
386
390
offsets[i] += 1 ;
@@ -414,6 +418,7 @@ static void buildDisplayLayout(const QTextBlock& block, LayoutBlockData* blockDa
414
418
// for each control char we injected that prevents font merging.
415
419
QTextLayout::FormatRange r;
416
420
r.format .setFontStyleStrategy (QFont::NoFontMerging);
421
+ r.format .setFontPointSize (0.01 );
417
422
418
423
for (const auto & [pos, count] : isolatesStartingAt.asKeyValueRange ()) {
419
424
r.start = pos + offsets[pos] - count;
@@ -426,7 +431,7 @@ static void buildDisplayLayout(const QTextBlock& block, LayoutBlockData* blockDa
426
431
formats.append (r);
427
432
}
428
433
429
- blockData->displayLayout = std::make_unique<QTextLayout>(text );
434
+ blockData->displayLayout = std::make_unique<QTextLayout>(blockText );
430
435
blockData->displayLayout ->setFormats (formats);
431
436
}
432
437
@@ -438,24 +443,28 @@ void EditorLayout::layoutBlock(QTextBlock& block, qreal topY)
438
443
BlockData::set<LayoutBlockData>(block, blockData);
439
444
}
440
445
441
- Qt::LayoutDirection dir = getBlockDirection (block);
446
+ QString blockText = block.text ();
447
+ Qt::LayoutDirection dir = getBlockDirection (block, blockText);
442
448
443
449
QTextOption option = document ()->defaultTextOption ();
444
450
option.setTextDirection (dir);
445
451
option.setAlignment (QStyle::visualAlignment (dir, option.alignment ()));
446
452
453
+ bool inContent = d_codeModel->canStartWithListItem (block);
454
+ qreal wrappingIndentWidth = calculateIndentWidth (blockText, option, inContent);
455
+
447
456
QTextLayout* defaultLayout = block.layout ();
448
- doBlockLayout (defaultLayout, option, topY);
457
+ doBlockLayout (defaultLayout, option, wrappingIndentWidth, topY);
449
458
block.setLineCount (defaultLayout->lineCount ());
450
459
451
- buildDisplayLayout (block, blockData);
460
+ buildDisplayLayout (block, blockText, blockData);
452
461
453
462
QTextLayout* displayLayout = blockData->displayLayout .get ();
454
463
if (displayLayout != nullptr ) {
455
464
displayLayout->setFont (document ()->defaultFont ());
456
465
displayLayout->setCursorMoveStyle (document ()->defaultCursorMoveStyle ());
457
466
458
- doBlockLayout (displayLayout, option, topY);
467
+ doBlockLayout (displayLayout, option, wrappingIndentWidth, topY);
459
468
460
469
if (displayLayout->boundingRect () != defaultLayout->boundingRect ()) {
461
470
qWarning () << " Block" << block.blockNumber () << " display bounding rect differs from default one!"
@@ -464,7 +473,11 @@ void EditorLayout::layoutBlock(QTextBlock& block, qreal topY)
464
473
}
465
474
}
466
475
467
- void EditorLayout::doBlockLayout (QTextLayout* layout, const QTextOption& option, qreal topY)
476
+ void EditorLayout::doBlockLayout (
477
+ QTextLayout* layout,
478
+ const QTextOption& option,
479
+ qreal wrappingIndentWidth,
480
+ qreal topY)
468
481
{
469
482
qreal margin = document ()->documentMargin ();
470
483
qreal availableWidth = document ()->textWidth () - 2 * margin;
@@ -473,6 +486,7 @@ void EditorLayout::doBlockLayout(QTextLayout* layout, const QTextOption& option,
473
486
layout->beginLayout ();
474
487
475
488
qreal lineHeight = 0 ;
489
+ bool first = true ;
476
490
477
491
while (true ) {
478
492
QTextLine line = layout->createLine ();
@@ -481,8 +495,25 @@ void EditorLayout::doBlockLayout(QTextLayout* layout, const QTextOption& option,
481
495
}
482
496
483
497
line.setLeadingIncluded (true );
484
- line.setLineWidth (availableWidth);
485
- line.setPosition (QPointF (0 , lineHeight));
498
+
499
+ if (first) {
500
+ line.setLineWidth (availableWidth);
501
+ line.setPosition (QPointF (0 , lineHeight));
502
+ first = false ;
503
+ }
504
+ else {
505
+ qreal restrictedWidth = qMax (
506
+ availableWidth - wrappingIndentWidth,
507
+ 0.2 * availableWidth);
508
+
509
+ line.setLineWidth (restrictedWidth);
510
+ if (option.textDirection () == Qt::RightToLeft) {
511
+ line.setPosition (QPointF (0 , lineHeight));
512
+ }
513
+ else {
514
+ line.setPosition (QPointF (availableWidth - restrictedWidth, lineHeight));
515
+ }
516
+ }
486
517
487
518
lineHeight += line.height ();
488
519
}
@@ -491,9 +522,9 @@ void EditorLayout::doBlockLayout(QTextLayout* layout, const QTextOption& option,
491
522
layout->endLayout ();
492
523
}
493
524
494
- Qt::LayoutDirection EditorLayout::getBlockDirection (const QTextBlock& block)
525
+ Qt::LayoutDirection EditorLayout::getBlockDirection (const QTextBlock& block, const QString& blockText )
495
526
{
496
- Qt::LayoutDirection dir = utils::naturalTextDirection (block. text () );
527
+ Qt::LayoutDirection dir = utils::naturalTextDirection (blockText );
497
528
if (dir != Qt::LayoutDirectionAuto) {
498
529
return dir;
499
530
}
@@ -520,6 +551,39 @@ Qt::LayoutDirection EditorLayout::getBlockDirection(const QTextBlock& block)
520
551
return qGuiApp->layoutDirection ();
521
552
}
522
553
554
+ qreal EditorLayout::calculateIndentWidth (const QString& text, const QTextOption& option, bool inContent)
555
+ {
556
+ qreal controlCharsWidth = 0 ;
557
+ QFontMetricsF ccFontMetrics { QFont (utils::CONTROL_FONT_FAMILY) };
558
+
559
+ QString prefix;
560
+ prefix.reserve (qRound (text.size () * 0.2 ));
561
+
562
+ for (QChar ch : text) {
563
+ if (utils::isWhitespace (ch)) {
564
+ if (utils::isSingleBidiMark (ch)) {
565
+ #if QT_VERSION >= QT_VERSION_CHECK(6, 9, 0)
566
+ if (option.flags () & QTextOption::ShowDefaultIgnorables) {
567
+ controlCharsWidth += ccFontMetrics.horizontalAdvance (ch);
568
+ }
569
+ #endif
570
+ }
571
+ else {
572
+ prefix.append (ch);
573
+ }
574
+ }
575
+ else if (inContent && (ch == ' -' || ch == ' +' )) {
576
+ prefix.append (ch);
577
+ }
578
+ else {
579
+ break ;
580
+ }
581
+ }
582
+
583
+ QFontMetricsF metrics { document ()->defaultFont () };
584
+ return metrics.horizontalAdvance (prefix, option) + controlCharsWidth;
585
+ }
586
+
523
587
QTextBlock EditorLayout::findContainingBlock (qreal y) const
524
588
{
525
589
// Skip top margin
0 commit comments