Changeset View
Changeset View
Standalone View
Standalone View
src/render/katerenderer.cpp
Show First 20 Lines • Show All 530 Lines • ▼ Show 20 Line(s) | 530 | { | |||
---|---|---|---|---|---|
531 | if (attribute.hasProperty(SelectedForeground)) { | 531 | if (attribute.hasProperty(SelectedForeground)) { | ||
532 | target.format.setForeground(attribute.selectedForeground()); | 532 | target.format.setForeground(attribute.selectedForeground()); | ||
533 | } | 533 | } | ||
534 | if (attribute.hasProperty(SelectedBackground)) { | 534 | if (attribute.hasProperty(SelectedBackground)) { | ||
535 | target.format.setBackground(attribute.selectedBackground()); | 535 | target.format.setBackground(attribute.selectedBackground()); | ||
536 | } | 536 | } | ||
537 | } | 537 | } | ||
538 | 538 | | |||
539 | static QVector<QTextLayout::FormatRange> decorationsForLineGap(const QVector<QTextLayout::FormatRange> &formats, const QVector<QTextLayout::FormatRange> &additionalFormats) { | ||||
540 | // We want to make sure that we draw no text for gap, but keep all the | ||||
541 | // existing background. Based on the logic in QTextLayout::draw, color | ||||
542 | // without foreground will not draw text. | ||||
543 | QVector<QTextLayout::FormatRange> gapFormats = formats + additionalFormats; | ||||
544 | for (auto &format : gapFormats) { | ||||
545 | format.format.clearForeground(); | ||||
546 | } | ||||
547 | // Add a dummy selection at the end of formats. It will draw "transparent" | ||||
548 | // text and background. so no text and background will should be drawn after | ||||
549 | // hanlding it. | ||||
550 | gapFormats.append(QTextLayout::FormatRange()); | ||||
551 | gapFormats.back().start = 0; | ||||
552 | gapFormats.back().length = INT_MAX; | ||||
553 | gapFormats.back().format.setForeground(Qt::transparent); | ||||
554 | gapFormats.back().format.setBackground(Qt::transparent); | ||||
555 | return gapFormats; | ||||
556 | } | ||||
557 | | ||||
539 | void KateRenderer::paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor, PaintTextLineFlags flags) | 558 | void KateRenderer::paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor, PaintTextLineFlags flags) | ||
540 | { | 559 | { | ||
541 | Q_ASSERT(range->isValid()); | 560 | Q_ASSERT(range->isValid()); | ||
542 | 561 | | |||
543 | // qCDebug(LOG_KTE)<<"KateRenderer::paintTextLine"; | 562 | // qCDebug(LOG_KTE)<<"KateRenderer::paintTextLine"; | ||
544 | 563 | | |||
545 | // font data | 564 | // font data | ||
546 | const QFontMetricsF &fm = m_fontMetrics; | 565 | const QFontMetricsF &fm = m_fontMetrics; | ||
Show All 37 Lines | 601 | } else { | |||
584 | KateTextLayout selectionLine = range->viewLine(range->viewLineForColumn(selectionStartColumn)); | 603 | KateTextLayout selectionLine = range->viewLine(range->viewLineForColumn(selectionStartColumn)); | ||
585 | int selectionX = cursorToX(selectionLine, selectionStartColumn, true); | 604 | int selectionX = cursorToX(selectionLine, selectionStartColumn, true); | ||
586 | paint.fillRect(QRect(selectionX - xStart, (int)selectionLine.lineLayout().y(), selectStickWidth, lineHeight()), selectionBrush); | 605 | paint.fillRect(QRect(selectionX - xStart, (int)selectionLine.lineLayout().y(), selectStickWidth, lineHeight()), selectionBrush); | ||
587 | } | 606 | } | ||
588 | } | 607 | } | ||
589 | 608 | | |||
590 | QVector<QTextLayout::FormatRange> additionalFormats; | 609 | QVector<QTextLayout::FormatRange> additionalFormats; | ||
591 | if (range->length() > 0) { | 610 | if (range->length() > 0) { | ||
611 | if (drawSelection) { | ||||
612 | additionalFormats = decorationsForLine(range->textLine(), range->line(), true); | ||||
613 | } | ||||
614 | | ||||
615 | // Check if there's any format need to be fill into the gap. | ||||
616 | auto formats = range->layout()->formats(); | ||||
617 | if (!formats.isEmpty() || !additionalFormats.isEmpty()) { | ||||
618 | QVector<QTextLayout::FormatRange> gapFormats; | ||||
619 | for (int i = 0; i < range->layout()->lineCount(); i++) { | ||||
620 | auto line = range->layout()->lineAt(i); | ||||
621 | // Right now, y should always be multiply of lineHeight. | ||||
622 | auto yOffset = qRound(line.y()) % lineHeight(); | ||||
623 | if (yOffset == 0) { | ||||
624 | continue; | ||||
625 | } | ||||
626 | auto rect = line.rect(); | ||||
627 | if (rect.height() <= 0) { | ||||
628 | continue; | ||||
629 | } | ||||
630 | auto textHeight = rect.height(); | ||||
631 | // Only initialize gapFormats once, it will never be empty. | ||||
632 | if (gapFormats.isEmpty()) { | ||||
633 | gapFormats = decorationsForLineGap(formats, additionalFormats); | ||||
634 | } | ||||
635 | // The line looks like | ||||
636 | // --- y: lineHeight * (i-1) | ||||
637 | // gap | ||||
638 | // --- y: line.y() = lineHeight * (i-1) + yOffset | ||||
639 | // text | ||||
640 | // --- y: lineHeight * i | ||||
641 | // We want to repeatedly draw "transparent" text in the gap region. | ||||
642 | // First we set the clip region to only cover the gap. | ||||
643 | rect.moveTop(line.y() - yOffset); | ||||
644 | rect.setHeight(yOffset); | ||||
645 | auto yStart = -yOffset; | ||||
646 | // We use a loop here to fill the yOffset gap with multiple textHeight. | ||||
647 | // Under most case, this should be only 0 or 1 extra transparent text | ||||
648 | // need to be drawn. | ||||
649 | while (yStart < 0) { | ||||
650 | range->layout()->draw(&paint, QPoint(-xStart, yStart), gapFormats, rect); | ||||
651 | yStart += textHeight; | ||||
652 | } | ||||
653 | } | ||||
654 | } | ||||
592 | // We may have changed the pen, be absolutely sure it gets set back to | 655 | // We may have changed the pen, be absolutely sure it gets set back to | ||
593 | // normal foreground color before drawing text for text that does not | 656 | // normal foreground color before drawing text for text that does not | ||
594 | // set the pen color | 657 | // set the pen color | ||
595 | paint.setPen(attribute(KTextEditor::dsNormal)->foreground().color()); | 658 | paint.setPen(attribute(KTextEditor::dsNormal)->foreground().color()); | ||
596 | // Draw the text :) | 659 | // Draw the text :) | ||
597 | if (drawSelection) { | | |||
598 | additionalFormats = decorationsForLine(range->textLine(), range->line(), true); | | |||
599 | range->layout()->draw(&paint, QPoint(-xStart, 0), additionalFormats); | 660 | range->layout()->draw(&paint, QPoint(-xStart, 0), additionalFormats); | ||
600 | | ||||
601 | } else { | | |||
602 | range->layout()->draw(&paint, QPoint(-xStart, 0)); | | |||
603 | } | | |||
604 | } | 661 | } | ||
605 | 662 | | |||
606 | QBrush backgroundBrush; | 663 | QBrush backgroundBrush; | ||
607 | bool backgroundBrushSet = false; | 664 | bool backgroundBrushSet = false; | ||
608 | 665 | | |||
609 | // Loop each individual line for additional text decoration etc. | 666 | // Loop each individual line for additional text decoration etc. | ||
610 | QVectorIterator<QTextLayout::FormatRange> it = range->layout()->formats(); | 667 | QVectorIterator<QTextLayout::FormatRange> it = range->layout()->formats(); | ||
611 | QVectorIterator<QTextLayout::FormatRange> it2 = additionalFormats; | 668 | QVectorIterator<QTextLayout::FormatRange> it2 = additionalFormats; | ||
▲ Show 20 Lines • Show All 276 Lines • ▼ Show 20 Line(s) | 920 | for (const auto &inlineNoteData : inlineNotes) { | |||
888 | inlineNote.provider()->paintInlineNote(inlineNote, paint); | 945 | inlineNote.provider()->paintInlineNote(inlineNote, paint); | ||
889 | paint.restore(); | 946 | paint.restore(); | ||
890 | } | 947 | } | ||
891 | } | 948 | } | ||
892 | } | 949 | } | ||
893 | 950 | | |||
894 | uint KateRenderer::fontHeight() const | 951 | uint KateRenderer::fontHeight() const | ||
895 | { | 952 | { | ||
896 | return m_fontHeight; | 953 | return qMax(m_textHeight, m_fontHeight); | ||
897 | } | 954 | } | ||
898 | 955 | | |||
899 | uint KateRenderer::documentHeight() const | 956 | uint KateRenderer::documentHeight() const | ||
900 | { | 957 | { | ||
901 | return m_doc->lines() * lineHeight(); | 958 | return m_doc->lines() * lineHeight(); | ||
902 | } | 959 | } | ||
903 | 960 | | |||
904 | int KateRenderer::lineHeight() const | 961 | int KateRenderer::lineHeight() const | ||
▲ Show 20 Lines • Show All 54 Lines • ▼ Show 20 Line(s) | |||||
959 | 1016 | | |||
960 | void KateRenderer::updateFontHeight() | 1017 | void KateRenderer::updateFontHeight() | ||
961 | { | 1018 | { | ||
962 | /** | 1019 | /** | ||
963 | * cache font + metrics | 1020 | * cache font + metrics | ||
964 | */ | 1021 | */ | ||
965 | m_font = config()->baseFont(); | 1022 | m_font = config()->baseFont(); | ||
966 | m_fontMetrics = QFontMetricsF(m_font); | 1023 | m_fontMetrics = QFontMetricsF(m_font); | ||
1024 | // Clear the text height based on font. | ||||
1025 | m_textHeight = -1; | ||||
967 | 1026 | | |||
968 | /** | 1027 | /** | ||
969 | * ensure minimal height of one pixel to not fall in the div by 0 trap somewhere | 1028 | * ensure minimal height of one pixel to not fall in the div by 0 trap somewhere | ||
970 | * | 1029 | * | ||
971 | * use a line spacing that matches the code in qt to layout/paint text | 1030 | * use a line spacing that matches the code in qt to layout/paint text | ||
972 | * | 1031 | * | ||
973 | * see bug 403868 | 1032 | * see bug 403868 | ||
974 | * https://github.com/qt/qtbase/blob/5.12/src/gui/text/qtextlayout.cpp (line 2270 at the moment) where the text height is set as: | 1033 | * https://github.com/qt/qtbase/blob/5.12/src/gui/text/qtextlayout.cpp (line 2270 at the moment) where the text height is set as: | ||
975 | * | 1034 | * | ||
976 | * qreal height = maxY + fontHeight - minY; | 1035 | * qreal height = maxY + fontHeight - minY; | ||
977 | * | 1036 | * | ||
978 | * with fontHeight: | 1037 | * with fontHeight: | ||
979 | * | 1038 | * | ||
980 | * qreal fontHeight = font.ascent() + font.descent(); | 1039 | * qreal fontHeight = font.ascent() + font.descent(); | ||
981 | */ | 1040 | */ | ||
982 | m_fontHeight = qMax(1, qCeil(m_fontMetrics.ascent() + m_fontMetrics.descent())); | 1041 | m_fontHeight = qMax(1, qCeil(m_fontMetrics.ascent() + m_fontMetrics.descent())); | ||
983 | } | 1042 | } | ||
pshinjo: I think this causes the regression mentioned in
>>! In D25339#663322, @rjvbb wrote:
> I can't… | |||||
984 | 1043 | | |||
985 | void KateRenderer::updateMarkerSize() | 1044 | void KateRenderer::updateMarkerSize() | ||
986 | { | 1045 | { | ||
987 | // marker size will be calculated over the value defined | 1046 | // marker size will be calculated over the value defined | ||
988 | // on dialog | 1047 | // on dialog | ||
989 | 1048 | | |||
990 | m_markerSize = spaceWidth() / (3.5 - (m_doc->config()->markerSize() * 0.5)); | 1049 | m_markerSize = spaceWidth() / (3.5 - (m_doc->config()->markerSize() * 0.5)); | ||
991 | } | 1050 | } | ||
▲ Show 20 Lines • Show All 76 Lines • ▼ Show 20 Line(s) | 1058 | { | |||
1068 | } | 1127 | } | ||
1069 | l->setFormats(decorations); | 1128 | l->setFormats(decorations); | ||
1070 | 1129 | | |||
1071 | // Begin layouting | 1130 | // Begin layouting | ||
1072 | l->beginLayout(); | 1131 | l->beginLayout(); | ||
1073 | 1132 | | |||
1074 | int height = 0; | 1133 | int height = 0; | ||
1075 | int shiftX = 0; | 1134 | int shiftX = 0; | ||
1135 | int pendingTextHeight = -1; | ||||
1076 | 1136 | | |||
1077 | bool needShiftX = (maxwidth != -1) && m_view && (m_view->config()->dynWordWrapAlignIndent() > 0); | 1137 | bool needShiftX = (maxwidth != -1) && m_view && (m_view->config()->dynWordWrapAlignIndent() > 0); | ||
1078 | 1138 | | |||
1079 | forever { | 1139 | forever { | ||
1080 | QTextLine line = l->createLine(); | 1140 | QTextLine line = l->createLine(); | ||
1081 | if (!line.isValid()) { | 1141 | if (!line.isValid()) { | ||
1082 | break; | 1142 | break; | ||
1083 | } | 1143 | } | ||
1084 | 1144 | | |||
1085 | if (maxwidth > 0) { | 1145 | if (maxwidth > 0) { | ||
1086 | line.setLineWidth(maxwidth); | 1146 | line.setLineWidth(maxwidth); | ||
1147 | } else { | ||||
1148 | line.setLineWidth(INT_MAX); | ||||
1087 | } | 1149 | } | ||
1088 | 1150 | | |||
1089 | // we include the leading, this must match the ::updateFontHeight code! | 1151 | // we include the leading, this must match the ::updateFontHeight code! | ||
1090 | line.setLeadingIncluded(true); | 1152 | line.setLeadingIncluded(true); | ||
1091 | 1153 | qreal lineOffset = 0; | |||
1092 | line.setPosition(QPoint(line.lineNumber() ? shiftX : firstLineOffset, height)); | 1154 | if (line.rect().isValid()) { | ||
1155 | if (line.height() < lineHeight()) { | ||||
1156 | lineOffset = lineHeight() - line.height(); | ||||
1157 | } | ||||
1158 | pendingTextHeight = qMax(qCeil(line.height()), pendingTextHeight); | ||||
1159 | } | ||||
1160 | line.setPosition(QPointF(line.lineNumber() ? shiftX : firstLineOffset, height + lineOffset)); | ||||
1093 | 1161 | | |||
1094 | if (needShiftX && line.width() > 0) { | 1162 | if (needShiftX && line.width() > 0) { | ||
1095 | needShiftX = false; | 1163 | needShiftX = false; | ||
1096 | // Determine x offset for subsequent-lines-of-paragraph indenting | 1164 | // Determine x offset for subsequent-lines-of-paragraph indenting | ||
1097 | int pos = textLine->nextNonSpaceChar(0); | 1165 | int pos = textLine->nextNonSpaceChar(0); | ||
1098 | 1166 | | |||
1099 | if (pos > 0) { | 1167 | if (pos > 0) { | ||
1100 | shiftX = (int)line.cursorToX(pos); | 1168 | shiftX = (int)line.cursorToX(pos); | ||
Show All 11 Lines | |||||
1112 | } | 1180 | } | ||
1113 | 1181 | | |||
1114 | height += lineHeight(); | 1182 | height += lineHeight(); | ||
1115 | } | 1183 | } | ||
1116 | 1184 | | |||
1117 | l->endLayout(); | 1185 | l->endLayout(); | ||
1118 | 1186 | | |||
1119 | lineLayout->setLayout(l); | 1187 | lineLayout->setLayout(l); | ||
1188 | | ||||
1189 | if (cacheLayout && pendingTextHeight > lineHeight()) { | ||||
1190 | m_textHeight = pendingTextHeight; | ||||
1191 | // trigger view update, if any! | ||||
1192 | QMetaObject::invokeMethod(m_view, "updateRendererConfig", Qt::QueuedConnection); | ||||
anthonyfieroni: Can you use functor here, instead of string. | |||||
1193 | } | ||||
1120 | } | 1194 | } | ||
1121 | 1195 | | |||
1122 | // 1) QString::isRightToLeft() sux | 1196 | // 1) QString::isRightToLeft() sux | ||
1123 | // 2) QString::isRightToLeft() is marked as internal (WTF?) | 1197 | // 2) QString::isRightToLeft() is marked as internal (WTF?) | ||
1124 | // 3) QString::isRightToLeft() does not seem to work on my setup | 1198 | // 3) QString::isRightToLeft() does not seem to work on my setup | ||
1125 | // 4) isStringRightToLeft() should behave much better than QString::isRightToLeft() therefore: | 1199 | // 4) isStringRightToLeft() should behave much better than QString::isRightToLeft() therefore: | ||
1126 | // 5) isStringRightToLeft() kicks ass | 1200 | // 5) isStringRightToLeft() kicks ass | ||
1127 | bool KateRenderer::isLineRightToLeft(KateLineLayoutPtr lineLayout) const | 1201 | bool KateRenderer::isLineRightToLeft(KateLineLayoutPtr lineLayout) const | ||
▲ Show 20 Lines • Show All 78 Lines • Show Last 20 Lines |
I think this causes the regression mentioned in
But this part is the raison d'etre for this patch: provide enough font height for non-latin-1 text. Also not considered is glyphs from other scripts, such as Cyrillic, Arabic, ... What could be another solution?