Index: src/view/kateviewhelpers.h =================================================================== --- src/view/kateviewhelpers.h +++ src/view/kateviewhelpers.h @@ -350,21 +350,26 @@ bool m_foldingMarkersOn: 1; bool m_dynWrapIndicatorsOn: 1; bool m_annotationBorderOn: 1; - + bool m_updatePositionToArea: 1; + + typedef QPair AreaPosition; + QVector m_positionToArea; + + const int m_separatorWidth = 2; + const int m_modAreaWidth = 3; + qreal m_maxCharWidth = 0.0; + int m_lineNumberAreaWidth = 0; + int m_iconAreaWidth = 0; + int m_foldingAreaWidth = 0; + int m_annotationAreaWidth = 0; const QChar m_dynWrapIndicatorChar = QChar(0x21AA); - int m_dynWrapIndicators; - int m_lastClickedLine; - int m_cachedLNWidth; - qreal m_maxCharWidth; - int iconPaneWidth; - int m_annotationBorderWidth; + int m_dynWrapIndicators = 0; + int m_lastClickedLine = -1; KTextEditor::AbstractAnnotationItemDelegate *m_annotationItemDelegate; bool m_hasUniformAnnotationItemSizes = false; bool m_isDefaultAnnotationItemDelegate = true; - mutable QColor m_oldBackgroundColor; - QPointer m_foldingPreview; KTextEditor::MovingRange *m_foldingRange; int m_nextHighlightBlock; Index: src/view/kateviewhelpers.cpp =================================================================== --- src/view/kateviewhelpers.cpp +++ src/view/kateviewhelpers.cpp @@ -1433,12 +1433,7 @@ , m_foldingMarkersOn(false) , m_dynWrapIndicatorsOn(false) , m_annotationBorderOn(false) - , m_dynWrapIndicators(0) - , m_lastClickedLine(-1) - , m_cachedLNWidth(0) - , m_maxCharWidth(0.0) - , iconPaneWidth(16) - , m_annotationBorderWidth(6) + , m_updatePositionToArea(true) , m_annotationItemDelegate(new KateAnnotationItemDelegate(m_viewInternal, this)) , m_foldingPreview(nullptr) , m_foldingRange(nullptr) @@ -1479,7 +1474,7 @@ m_iconBorderOn = enable; - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } @@ -1500,7 +1495,7 @@ emit m_view->annotationBorderVisibilityChanged(m_view, enable); - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } @@ -1523,7 +1518,7 @@ m_lineNumbersOn = enable; m_dynWrapIndicatorsOn = (m_dynWrapIndicators == 1) ? enable : m_dynWrapIndicators; - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } @@ -1539,7 +1534,7 @@ * We don't have to touch the m_dynWrapIndicatorsOn because * we already got it right from the m_lineNumbersOn */ - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot( 0, this, SLOT(update()) ); } @@ -1563,7 +1558,7 @@ m_dynWrapIndicators = state; m_dynWrapIndicatorsOn = (state == 1) ? m_lineNumbersOn : state; - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } @@ -1576,39 +1571,20 @@ m_foldingMarkersOn = enable; - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } QSize KateIconBorder::sizeHint() const { - int w = 0; - - if (m_iconBorderOn) { - w += iconPaneWidth + 2; - } - - if (m_annotationBorderOn) { - w += m_annotationBorderWidth + 2; - } - - if (m_lineNumbersOn || (m_dynWrapIndicatorsOn && m_view->config()->dynWordWrap())) { - w += lineNumberWidth() + 2; - } - - if (m_foldingMarkersOn) { - w += iconPaneWidth; - } + int w = 1; // Must be any value != 0 or we will never painted! - // space for the line modification system border - if (m_view->config()->lineModification()) { - w += 3; + const int i = m_positionToArea.size(); + if (i > 0) { + w = m_positionToArea.at(i -1).first; } - // two pixel space - w += 2; - return QSize(w, 0); } @@ -1631,11 +1607,14 @@ // "Line Numbers Off" but all these width calculating looks slightly hacky // the icon pane scales with the font... - iconPaneWidth = fm.height(); + m_iconAreaWidth = fm.height(); + + // Only for now, later may that become an own value + m_foldingAreaWidth = m_iconAreaWidth; calcAnnotationBorderWidth(); - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } @@ -1681,7 +1660,8 @@ qreal size = qMin(width, height); - if (KColorUtils::luma(c) > 0.25) { + // Paint unfolded icon less pushy + if (open) { // if (KColorUtils::luma(c) > 0.25) { c = KColorUtils::darken(c); } else { c = KColorUtils::shade(c, 0.1); @@ -1924,36 +1904,22 @@ void KateIconBorder::paintBorder(int /*x*/, int y, int /*width*/, int height) { - uint h = m_view->renderer()->lineHeight(); - uint startz = (y / h); - uint endz = startz + 1 + (height / h); - uint lineRangesSize = m_viewInternal->cache()->viewCacheLineCount(); - uint currentLine = m_view->cursorPosition().line(); + const uint h = m_view->renderer()->lineHeight(); + const uint startz = (y / h); + const uint endz = qMin(startz + 1 + (height / h), static_cast(m_viewInternal->cache()->viewCacheLineCount())); + const uint currentLine = m_view->cursorPosition().line(); // center the folding boxes int m_px = (h - 11) / 2; if (m_px < 0) { m_px = 0; } - int lnWidth(0); - if (m_lineNumbersOn || m_dynWrapIndicatorsOn) { // avoid calculating unless needed ;-) - lnWidth = lineNumberWidth(); - if (lnWidth != m_cachedLNWidth || m_oldBackgroundColor != m_view->renderer()->config()->iconBarColor()) { - // we went from n0 ->n9 lines or vice versa - // this causes an extra updateGeometry() first time the line numbers - // are displayed, but sizeHint() is supposed to be const so we can't set - // the cached value there. - m_cachedLNWidth = lnWidth; - m_oldBackgroundColor = m_view->renderer()->config()->iconBarColor(); - updateGeometry(); - update(); - return; - } + if (m_updatePositionToArea) { + m_lineNumberAreaWidth = lineNumberWidth(); + m_positionToArea.clear(); } - int w(this->width()); // sane value/calc only once - QPainter p(this); p.setRenderHints(QPainter::TextAntialiasing); p.setFont(m_view->renderer()->config()->font()); // for line numbers @@ -1963,232 +1929,227 @@ KateAnnotationGroupPositionState annotationGroupPositionState(m_viewInternal, model, m_hoveredAnnotationGroupIdentifier, startz, m_annotationBorderOn); - - for (uint z = startz; z <= endz; z++) { - int y = h * z; - int realLine = -1; - - if (z < lineRangesSize) { - realLine = m_viewInternal->cache()->viewLine(z).line(); + // Paint the border in chunks line by line + for (uint z = startz; z < endz; z++) { + const KateTextLayout lineLayout = m_viewInternal->cache()->viewLine(z); + int realLine = lineLayout.line(); + if (realLine < 0) { + // We have reached the end of the document, nothing more to do + break; } - int lnX = 0; - - p.fillRect(0, y, w - 5, h, m_view->renderer()->config()->iconBarColor()); - p.fillRect(w - 5, y, 5, h, m_view->renderer()->config()->backgroundColor()); + // Painting coordinates, lineHeight * lineNumber + const uint y = h * z; + // Paint the border in chunks left->right, remember used width + uint lnX = 0; // icon pane if (m_iconBorderOn) { p.setPen(m_view->renderer()->config()->separatorColor()); p.setBrush(m_view->renderer()->config()->separatorColor()); - p.drawLine(lnX + iconPaneWidth + 1, y, lnX + iconPaneWidth + 1, y + h); - - if ((realLine > -1) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { - uint mrk(m_doc->mark(realLine)); // call only once - - if (mrk) { - for (uint bit = 0; bit < 32; bit++) { - MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1 << bit); - if (mrk & markType) { - QPixmap px_mark(m_doc->markPixmap(markType)); - px_mark.setDevicePixelRatio(devicePixelRatioF()); - - if (!px_mark.isNull() && h > 0 && iconPaneWidth > 0) { - // scale up to a usable size - px_mark = px_mark.scaled(iconPaneWidth * devicePixelRatio(), h * devicePixelRatio(), Qt::KeepAspectRatio); - - // center the mark pixmap - int x_px = (iconPaneWidth - px_mark.width()/ devicePixelRatio()) / 2; - if (x_px < 0) { - x_px = 0; - } - - int y_px = (h - px_mark.height() / devicePixelRatio()) / 2; - if (y_px < 0) { - y_px = 0; - } - - p.drawPixmap(lnX + x_px, y + y_px, px_mark); + p.drawLine(lnX + m_iconAreaWidth, y, lnX + m_iconAreaWidth, y + h); + + const uint mrk(m_doc->mark(realLine)); // call only once + if (mrk && lineLayout.startCol() == 0) { + for (uint bit = 0; bit < 32; bit++) { + MarkInterface::MarkTypes markType = (MarkInterface::MarkTypes)(1 << bit); + if (mrk & markType) { + QPixmap px_mark(m_doc->markPixmap(markType)); + px_mark.setDevicePixelRatio(devicePixelRatioF()); + + if (!px_mark.isNull() && h > 0 && m_iconAreaWidth > 0) { + // scale up to a usable size + px_mark = px_mark.scaled(m_iconAreaWidth * devicePixelRatio(), h * devicePixelRatio(), Qt::KeepAspectRatio); + + // center the mark pixmap + int x_px = (m_iconAreaWidth - px_mark.width()/ devicePixelRatio()) / 2; + if (x_px < 0) { + x_px = 0; + } + + int y_px = (h - px_mark.height() / devicePixelRatio()) / 2; + if (y_px < 0) { + y_px = 0; } + + p.drawPixmap(lnX + x_px, y + y_px, px_mark); } } } } - lnX += iconPaneWidth + 2; + lnX += m_iconAreaWidth + m_separatorWidth; + if (m_updatePositionToArea) { + m_positionToArea.append(AreaPosition(lnX, IconBorder)); + } } // annotation information if (m_annotationBorderOn) { // Draw a border line between annotations and the line numbers p.setPen(m_view->renderer()->config()->lineNumberColor()); p.setBrush(m_view->renderer()->config()->lineNumberColor()); - const qreal borderX = lnX + m_annotationBorderWidth + 0.5; + const qreal borderX = lnX + m_annotationAreaWidth + 0.5; p.drawLine(QPointF(borderX, y+0.5), QPointF(borderX, y + h - 0.5)); - if ((realLine > -1) && model) { + if (model) { KTextEditor::StyleOptionAnnotationItem styleOption; initStyleOption(&styleOption); - styleOption.rect.setRect(lnX, y, m_annotationBorderWidth, h); + styleOption.rect.setRect(lnX, y, m_annotationAreaWidth, h); annotationGroupPositionState.nextLine(styleOption, z, realLine); m_annotationItemDelegate->paint(&p, styleOption, model, realLine); } - // adjust current X position - lnX += m_annotationBorderWidth + /* separator line width */1; + lnX += m_annotationAreaWidth + m_separatorWidth; + if (m_updatePositionToArea) { + m_positionToArea.append(AreaPosition(lnX, AnnotationBorder)); + } } // line number + QColor lineNumberColor; // Remember color for folding below if (m_lineNumbersOn || m_dynWrapIndicatorsOn) { - if (realLine > -1) { - int distanceToCurrent = abs(realLine - static_cast(currentLine)); - QColor color; - - if (distanceToCurrent == 0) { - color = m_view->renderer()->config()->currentLineNumberColor(); - } else { - color = m_view->renderer()->config()->lineNumberColor(); - } - p.setPen(color); - p.setBrush(color); - - if (m_viewInternal->cache()->viewLine(z).startCol() == 0) { - if (m_relLineNumbersOn) { - if (distanceToCurrent == 0) { - p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, - Qt::TextDontClip|Qt::AlignLeft|Qt::AlignVCenter, QString::number(realLine + 1)); - } else { - p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, - Qt::TextDontClip|Qt::AlignRight|Qt::AlignVCenter, QString::number(distanceToCurrent)); - } - if (m_updateRelLineNumbers) { - m_updateRelLineNumbers = false; - update(); - } - } else if (m_lineNumbersOn) { - p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, - Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, QString::number(realLine + 1)); + const int distanceToCurrent = abs(realLine - static_cast(currentLine)); + if (distanceToCurrent == 0) { + lineNumberColor = m_view->renderer()->config()->currentLineNumberColor(); + } else { + lineNumberColor = m_view->renderer()->config()->lineNumberColor(); + } + p.setPen(lineNumberColor); + p.setBrush(lineNumberColor); + + if (lineLayout.startCol() == 0) { + if (m_relLineNumbersOn) { + if (distanceToCurrent == 0) { + p.drawText(lnX + m_maxCharWidth / 2, y, m_lineNumberAreaWidth - m_maxCharWidth, h, + Qt::TextDontClip | Qt::AlignLeft | Qt::AlignVCenter, QString::number(realLine + 1)); + } else { + p.drawText(lnX + m_maxCharWidth / 2, y, m_lineNumberAreaWidth - m_maxCharWidth, h, + Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, QString::number(distanceToCurrent)); + } + if (m_updateRelLineNumbers) { + m_updateRelLineNumbers = false; + update(); } - } else if (m_dynWrapIndicatorsOn) { - p.drawText(lnX + m_maxCharWidth / 2, y, lnWidth - m_maxCharWidth, h, - Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, m_dynWrapIndicatorChar); + } else if (m_lineNumbersOn) { + p.drawText(lnX + m_maxCharWidth / 2, y, m_lineNumberAreaWidth - m_maxCharWidth, h, + Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, QString::number(realLine + 1)); } + } else if (m_dynWrapIndicatorsOn) { + p.drawText(lnX + m_maxCharWidth / 2, y, m_lineNumberAreaWidth - m_maxCharWidth, h, + Qt::TextDontClip | Qt::AlignRight | Qt::AlignVCenter, m_dynWrapIndicatorChar); + } + + lnX += m_lineNumberAreaWidth + m_separatorWidth; + if (m_updatePositionToArea) { + m_positionToArea.append(AreaPosition(lnX, LineNumbers)); + } + } + + // modified line system + if (m_view->config()->lineModification() && !m_doc->url().isEmpty()) { + + const Kate::TextLine tl = m_doc->plainKateTextLine(realLine); + if (tl->markedAsModified()) { + p.fillRect(lnX, y, m_modAreaWidth, h, m_view->renderer()->config()->modifiedLineColor()); + } else if (tl->markedAsSavedOnDisk()) { + p.fillRect(lnX, y, m_modAreaWidth, h, m_view->renderer()->config()->savedLineColor()); + } else { + p.fillRect(lnX, y, m_modAreaWidth, h, m_view->renderer()->config()->iconBarColor()); } - lnX += lnWidth + 2; + lnX += m_modAreaWidth; // No m_separatorWidth + if (m_updatePositionToArea) { + m_positionToArea.append(AreaPosition(lnX, None)); + } } // folding markers if (m_foldingMarkersOn) { // first icon border background - p.fillRect(lnX, y, iconPaneWidth, h, m_view->renderer()->config()->iconBarColor()); + p.fillRect(lnX, y, m_foldingAreaWidth, h, m_view->renderer()->config()->iconBarColor()); + const QColor foldingColor(m_view->renderer()->config()->foldingColor()); // possible additional folding highlighting - if ((realLine >= 0) && m_foldingRange && m_foldingRange->overlapsLine(realLine)) { + if (m_foldingRange && m_foldingRange->overlapsLine(realLine)) { p.save(); // use linear gradient as brush - QLinearGradient g(lnX, y, lnX + iconPaneWidth, y); - const QColor foldingColor(m_view->renderer()->config()->foldingColor()); + QLinearGradient g(lnX, y, lnX + m_foldingAreaWidth, y); g.setColorAt(0, foldingColor); g.setColorAt(0.3, foldingColor.lighter(110)); g.setColorAt(1, foldingColor); p.setBrush(g); p.setPen(foldingColor); - p.setClipRect(lnX, y, iconPaneWidth, h); + p.setClipRect(lnX, y, m_foldingAreaWidth, h); p.setRenderHint(QPainter::Antialiasing); const qreal r = 4.0; - if (m_foldingRange->start().line() == realLine && - m_viewInternal->cache()->viewLine(z).viewLine() == 0) { - p.drawRect(lnX, y, iconPaneWidth, h + r); + if (m_foldingRange->start().line() == realLine && lineLayout.viewLine() == 0) { + p.drawRect(lnX, y, m_foldingAreaWidth, h + r); } else if (m_foldingRange->end().line() == realLine && - m_viewInternal->cache()->viewLine(z).viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { - p.drawRect(lnX, y - r, iconPaneWidth, h + r); + lineLayout.viewLine() == m_viewInternal->cache()->viewLineCount(realLine) - 1) { + p.drawRect(lnX, y - r, m_foldingAreaWidth, h + r); } else { - p.drawRect(lnX, y - r, iconPaneWidth, h + 2 * r); + p.drawRect(lnX, y - r, m_foldingAreaWidth, h + 2 * r); } p.restore(); } - if ((realLine >= 0) && (m_viewInternal->cache()->viewLine(z).startCol() == 0)) { + if (lineLayout.startCol() == 0) { QVector > startingRanges = m_view->textFolding().foldingRangesStartingOnLine(realLine); bool anyFolded = false; - for (int i = 0; i < startingRanges.size(); ++i) + for (int i = 0; i < startingRanges.size(); ++i) { if (startingRanges[i].second & Kate::TextFolding::Folded) { anyFolded = true; } + } - Kate::TextLine tl = m_doc->kateTextLine(realLine); - + const Kate::TextLine tl = m_doc->kateTextLine(realLine); if (!startingRanges.isEmpty() || tl->markedAsFoldingStart()) { if (anyFolded) { - paintTriangle(p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, false); + paintTriangle(p, foldingColor, lnX, y, m_foldingAreaWidth, h, false); } else { - paintTriangle(p, m_view->renderer()->config()->foldingColor(), lnX, y, iconPaneWidth, h, true); + if (!lineNumberColor.isValid()) { // Try to reuse color from above + int distanceToCurrent = abs(realLine - static_cast(currentLine)); + if (distanceToCurrent == 0) { + lineNumberColor = m_view->renderer()->config()->currentLineNumberColor(); + } else { + lineNumberColor = m_view->renderer()->config()->lineNumberColor(); + } + } + paintTriangle(p, lineNumberColor, lnX, y, m_foldingAreaWidth, h, true); } } } - lnX += iconPaneWidth; + lnX += m_foldingAreaWidth; + if (m_updatePositionToArea) { + m_positionToArea.append(AreaPosition(lnX, FoldingMarkers)); + } } - // modified line system - if (m_view->config()->lineModification() && realLine > -1 && !m_doc->url().isEmpty()) { - // one pixel space - ++lnX; - - Kate::TextLine tl = m_doc->plainKateTextLine(realLine); - if (tl->markedAsModified()) { - p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->modifiedLineColor()); - } - if (tl->markedAsSavedOnDisk()) { - p.fillRect(lnX, y, 3, h, m_view->renderer()->config()->savedLineColor()); - } + if (m_updatePositionToArea) { + m_updatePositionToArea = false; + // Now that we know our needed space, ensure we are painted properly + updateGeometry(); + update(); + return; } } } KateIconBorder::BorderArea KateIconBorder::positionToArea(const QPoint &p) const { - // see KateIconBorder::sizeHint() for pixel spacings - int x = 0; - if (m_iconBorderOn) { - x += iconPaneWidth; - if (p.x() <= x) { - return IconBorder; - } - x += 2; - } - if (this->m_annotationBorderOn) { - x += m_annotationBorderWidth; - if (p.x() <= x) { - return AnnotationBorder; - } - x += 2; - } - if (m_lineNumbersOn || m_dynWrapIndicators) { - x += lineNumberWidth(); - if (p.x() <= x) { - return LineNumbers; - } - x += 2; - } - if (m_foldingMarkersOn) { - x += iconPaneWidth; - if (p.x() <= x) { - return FoldingMarkers; - } - } - if (m_view->config()->lineModification()) { - x += 3 + 2; - if (p.x() <= x) { - return ModificationBorder; + for (int i = 0; i < m_positionToArea.size(); ++i) { + if (p.x() <= m_positionToArea.at(i).first) { + return m_positionToArea.at(i).second; } } + return None; } @@ -2628,7 +2589,7 @@ { styleOption->initFrom(this); styleOption->view = m_view; - styleOption->decorationSize = QSize(iconPaneWidth, iconPaneWidth); + styleOption->decorationSize = QSize(m_iconAreaWidth, m_iconAreaWidth); styleOption->contentFontMetrics = m_view->renderer()->config()->fontMetrics(); } @@ -2653,11 +2614,11 @@ { int x = 0; if (m_iconBorderOn) { - x += iconPaneWidth + 2; + x += m_iconAreaWidth + 2; } const int y = m_view->m_viewInternal->lineToY(line); - return QRect(x, y, m_annotationBorderWidth, m_view->renderer()->lineHeight()); + return QRect(x, y, m_annotationAreaWidth, m_view->renderer()->lineHeight()); } void KateIconBorder::updateAnnotationLine(int line) @@ -2673,8 +2634,8 @@ width = m_annotationItemDelegate->sizeHint(styleOption, model, line).width(); } - if (width > m_annotationBorderWidth) { - m_annotationBorderWidth = width; + if (width > m_annotationAreaWidth) { + m_annotationAreaWidth = width; updateGeometry(); QTimer::singleShot(0, this, SLOT(update())); @@ -2702,15 +2663,15 @@ { calcAnnotationBorderWidth(); - updateGeometry(); + m_updatePositionToArea = true; QTimer::singleShot(0, this, SLOT(update())); } void KateIconBorder::calcAnnotationBorderWidth() { // TODO: another magic number, not matching the one in updateAnnotationLine() - m_annotationBorderWidth = 6; + m_annotationAreaWidth = 6; KTextEditor::AnnotationModel *model = m_view->annotationModel() ? m_view->annotationModel() : m_doc->annotationModel(); @@ -2723,8 +2684,8 @@ const int checkedLineCount = m_hasUniformAnnotationItemSizes ? 1 : lineCount; for (int i = 0; i < checkedLineCount; ++i) { const int curwidth = m_annotationItemDelegate->sizeHint(styleOption, model, i).width(); - if (curwidth > m_annotationBorderWidth) { - m_annotationBorderWidth = curwidth; + if (curwidth > m_annotationAreaWidth) { + m_annotationAreaWidth = curwidth; } } }