diff --git a/src/render/katerenderer.h b/src/render/katerenderer.h --- a/src/render/katerenderer.h +++ b/src/render/katerenderer.h @@ -422,6 +422,7 @@ int m_tabWidth; int m_indentWidth; int m_fontHeight; + mutable int m_textHeight = -1; // some internal flags KateRenderer::caretStyles m_caretStyle; diff --git a/src/render/katerenderer.cpp b/src/render/katerenderer.cpp --- a/src/render/katerenderer.cpp +++ b/src/render/katerenderer.cpp @@ -536,6 +536,25 @@ } } +static QVector decorationsForLineGap(const QVector &formats, const QVector &additionalFormats) { + // We want to make sure that we draw no text for gap, but keep all the + // existing background. Based on the logic in QTextLayout::draw, color + // without foreground will not draw text. + QVector gapFormats = formats + additionalFormats; + for (auto &format : gapFormats) { + format.format.clearForeground(); + } + // Add a dummy selection at the end of formats. It will draw "transparent" + // text and background. so no text and background will should be drawn after + // hanlding it. + gapFormats.append(QTextLayout::FormatRange()); + gapFormats.back().start = 0; + gapFormats.back().length = INT_MAX; + gapFormats.back().format.setForeground(Qt::transparent); + gapFormats.back().format.setBackground(Qt::transparent); + return gapFormats; +} + void KateRenderer::paintTextLine(QPainter &paint, KateLineLayoutPtr range, int xStart, int xEnd, const KTextEditor::Cursor *cursor, PaintTextLineFlags flags) { Q_ASSERT(range->isValid()); @@ -589,18 +608,56 @@ QVector additionalFormats; if (range->length() > 0) { + if (drawSelection) { + additionalFormats = decorationsForLine(range->textLine(), range->line(), true); + } + + // Check if there's any format need to be fill into the gap. + auto formats = range->layout()->formats(); + if (!formats.isEmpty() || !additionalFormats.isEmpty()) { + QVector gapFormats; + for (int i = 0; i < range->layout()->lineCount(); i++) { + auto line = range->layout()->lineAt(i); + // Right now, y should always be multiply of lineHeight. + auto yOffset = qRound(line.y()) % lineHeight(); + if (yOffset == 0) { + continue; + } + auto rect = line.rect(); + if (rect.height() <= 0) { + continue; + } + auto textHeight = rect.height(); + // Only initialize gapFormats once, it will never be empty. + if (gapFormats.isEmpty()) { + gapFormats = decorationsForLineGap(formats, additionalFormats); + } + // The line looks like + // --- y: lineHeight * (i-1) + // gap + // --- y: line.y() = lineHeight * (i-1) + yOffset + // text + // --- y: lineHeight * i + // We want to repeatedly draw "transparent" text in the gap region. + // First we set the clip region to only cover the gap. + rect.moveTop(line.y() - yOffset); + rect.setHeight(yOffset); + auto yStart = -yOffset; + // We use a loop here to fill the yOffset gap with multiple textHeight. + // Under most case, this should be only 0 or 1 extra transparent text + // need to be drawn. + while (yStart < 0) { + range->layout()->draw(&paint, QPoint(-xStart, yStart), gapFormats, rect); + yStart += textHeight; + } + } + } // We may have changed the pen, be absolutely sure it gets set back to // normal foreground color before drawing text for text that does not // set the pen color paint.setPen(attribute(KTextEditor::dsNormal)->foreground().color()); // Draw the text :) - if (drawSelection) { - additionalFormats = decorationsForLine(range->textLine(), range->line(), true); - range->layout()->draw(&paint, QPoint(-xStart, 0), additionalFormats); - - } else { - range->layout()->draw(&paint, QPoint(-xStart, 0)); - } + range->layout()->draw(&paint, QPoint(-xStart, 0), additionalFormats); } QBrush backgroundBrush; @@ -893,7 +950,7 @@ uint KateRenderer::fontHeight() const { - return m_fontHeight; + return qMax(m_textHeight, m_fontHeight); } uint KateRenderer::documentHeight() const @@ -964,6 +1021,8 @@ */ m_font = config()->baseFont(); m_fontMetrics = QFontMetricsF(m_font); + // Clear the text height based on font. + m_textHeight = -1; /** * ensure minimal height of one pixel to not fall in the div by 0 trap somewhere @@ -1073,6 +1132,7 @@ int height = 0; int shiftX = 0; + int pendingTextHeight = -1; bool needShiftX = (maxwidth != -1) && m_view && (m_view->config()->dynWordWrapAlignIndent() > 0); @@ -1084,12 +1144,20 @@ if (maxwidth > 0) { line.setLineWidth(maxwidth); + } else { + line.setLineWidth(INT_MAX); } // we include the leading, this must match the ::updateFontHeight code! line.setLeadingIncluded(true); - - line.setPosition(QPoint(line.lineNumber() ? shiftX : firstLineOffset, height)); + qreal lineOffset = 0; + if (line.rect().isValid()) { + if (line.height() < lineHeight()) { + lineOffset = lineHeight() - line.height(); + } + pendingTextHeight = qMax(qCeil(line.height()), pendingTextHeight); + } + line.setPosition(QPointF(line.lineNumber() ? shiftX : firstLineOffset, height + lineOffset)); if (needShiftX && line.width() > 0) { needShiftX = false; @@ -1117,6 +1185,12 @@ l->endLayout(); lineLayout->setLayout(l); + + if (cacheLayout && pendingTextHeight > lineHeight()) { + m_textHeight = pendingTextHeight; + // trigger view update, if any! + QMetaObject::invokeMethod(m_view, "updateRendererConfig", Qt::QueuedConnection); + } } // 1) QString::isRightToLeft() sux diff --git a/src/view/kateview.h b/src/view/kateview.h --- a/src/view/kateview.h +++ b/src/view/kateview.h @@ -799,7 +799,7 @@ void updateDocumentConfig(); - void updateRendererConfig(); + Q_INVOKABLE void updateRendererConfig(); private Q_SLOTS: void updateFoldingConfig();