Files
qt-creator/src/libs/utils/highlightingitemdelegate.cpp
Friedemann Kleint ec6b38dea0 Fix Qt 5.13 deprecation warning about QFontMetrics::width()
Replace by QFontMetrics::horizontalAdvance(), fixing:
warning: ‘int QFontMetrics::width(const QString&, int) const’ is deprecated:
 Use QFontMetrics::horizontalAdvance [-Wdeprecated-declarations]

Change-Id: I9991ffefe6e87e872dc35ba291d562e06b28ca64
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
2019-02-15 15:12:02 +00:00

320 lines
12 KiB
C++

/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "highlightingitemdelegate.h"
#include <QApplication>
#include <QModelIndex>
#include <QPainter>
const int kMinimumLineNumberDigits = 6;
namespace Utils {
HighlightingItemDelegate::HighlightingItemDelegate(int tabWidth, QObject *parent)
: QItemDelegate(parent)
{
setTabWidth(tabWidth);
}
void HighlightingItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
const QModelIndex &index) const
{
static const int iconSize = 16;
painter->save();
const QStyleOptionViewItem opt = setOptions(index, option);
painter->setFont(opt.font);
QItemDelegate::drawBackground(painter, opt, index);
// ---- do the layout
QRect checkRect;
QRect pixmapRect;
QRect textRect;
// check mark
const bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable);
Qt::CheckState checkState = Qt::Unchecked;
if (checkable) {
QVariant checkStateData = index.data(Qt::CheckStateRole);
checkState = static_cast<Qt::CheckState>(checkStateData.toInt());
checkRect = doCheck(opt, opt.rect, checkStateData);
}
// icon
const QIcon icon = index.model()->data(index, Qt::DecorationRole).value<QIcon>();
if (!icon.isNull()) {
const QSize size = icon.actualSize(QSize(iconSize, iconSize));
pixmapRect = QRect(0, 0, size.width(), size.height());
}
// text
textRect = opt.rect.adjusted(0, 0, checkRect.width() + pixmapRect.width(), 0);
// do layout
doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
// ---- draw the items
// icon
if (!icon.isNull())
icon.paint(painter, pixmapRect, option.decorationAlignment);
// line numbers
const int lineNumberAreaWidth = drawLineNumber(painter, opt, textRect, index);
textRect.adjust(lineNumberAreaWidth, 0, 0, 0);
// text and focus/selection
drawText(painter, opt, textRect, index);
QItemDelegate::drawFocus(painter, opt, opt.rect);
// check mark
if (checkable)
QItemDelegate::drawCheck(painter, opt, checkRect, checkState);
painter->restore();
}
void HighlightingItemDelegate::setTabWidth(int width)
{
m_tabString = QString(width, ' ');
}
// returns the width of the line number area
int HighlightingItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option,
const QRect &rect,
const QModelIndex &index) const
{
static const int lineNumberAreaHorizontalPadding = 4;
const int lineNumber = index.model()->data(index, int(HighlightingItemRole::LineNumber)).toInt();
if (lineNumber < 1)
return 0;
const bool isSelected = option.state & QStyle::State_Selected;
const QString lineText = QString::number(lineNumber);
const int minimumLineNumberDigits = qMax(kMinimumLineNumberDigits, lineText.count());
const int fontWidth =
painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, '0'));
const int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth
+ lineNumberAreaHorizontalPadding;
QRect lineNumberAreaRect(rect);
lineNumberAreaRect.setWidth(lineNumberAreaWidth);
QPalette::ColorGroup cg = QPalette::Normal;
if (!(option.state & QStyle::State_Active))
cg = QPalette::Inactive;
else if (!(option.state & QStyle::State_Enabled))
cg = QPalette::Disabled;
painter->fillRect(lineNumberAreaRect, QBrush(isSelected ?
option.palette.brush(cg, QPalette::Highlight) :
option.palette.color(cg, QPalette::Base).darker(111)));
QStyleOptionViewItem opt = option;
opt.displayAlignment = Qt::AlignRight | Qt::AlignVCenter;
opt.palette.setColor(cg, QPalette::Text, Qt::darkGray);
const QStyle *style = QApplication::style();
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, nullptr) + 1;
const QRect rowRect
= lineNumberAreaRect.adjusted(-textMargin, 0,
textMargin - lineNumberAreaHorizontalPadding, 0);
QItemDelegate::drawDisplay(painter, opt, rowRect, lineText);
return lineNumberAreaWidth;
}
void HighlightingItemDelegate::drawText(QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect,
const QModelIndex &index) const
{
QString text = index.model()->data(index, Qt::DisplayRole).toString();
// show number of subresults in displayString
if (index.model()->hasChildren(index))
text += " (" + QString::number(index.model()->rowCount(index)) + ')';
QVector<int> searchTermStarts =
index.model()->data(index, int(HighlightingItemRole::StartColumn)).value<QVector<int>>();
QVector<int> searchTermLengths =
index.model()->data(index, int(HighlightingItemRole::Length)).value<QVector<int>>();
if (searchTermStarts.isEmpty()) {
drawDisplay(painter, option, rect, text.replace('\t', m_tabString), {});
return;
}
// replace tabs with searchTerm bookkeeping
const int tabDiff = m_tabString.size() - 1;
for (int i = 0; i < text.length(); i++) {
if (text.at(i) != '\t')
continue;
text.replace(i, 1, m_tabString);
// adjust highlighting length if tab is highlighted
for (int j = 0; j < searchTermStarts.size(); ++j) {
if (i >= searchTermStarts.at(j) && i < searchTermStarts.at(j) + searchTermLengths.at(j))
searchTermLengths[j] += tabDiff;
}
// adjust all following highlighting starts
for (int j = 0; j < searchTermStarts.size(); ++j) {
if (searchTermStarts.at(j) > i)
searchTermStarts[j] += tabDiff;
}
i += tabDiff;
}
const QColor highlightForeground =
index.model()->data(index, int(HighlightingItemRole::Foreground)).value<QColor>();
const QColor highlightBackground =
index.model()->data(index, int(HighlightingItemRole::Background)).value<QColor>();
QTextCharFormat highlightFormat;
highlightFormat.setForeground(highlightForeground);
highlightFormat.setBackground(highlightBackground);
QVector<QTextLayout::FormatRange> formats;
for (int i = 0, size = searchTermStarts.size(); i < size; ++i)
formats.append({searchTermStarts.at(i), searchTermLengths.at(i), highlightFormat});
drawDisplay(painter, option, rect, text, formats);
}
// copied from QItemDelegate for drawDisplay
static QString replaceNewLine(QString text)
{
static const QChar nl = '\n';
for (int i = 0; i < text.count(); ++i)
if (text.at(i) == nl)
text[i] = QChar::LineSeparator;
return text;
}
// copied from QItemDelegate for drawDisplay
QSizeF doTextLayout(QTextLayout *textLayout, int lineWidth)
{
qreal height = 0;
qreal widthUsed = 0;
textLayout->beginLayout();
while (true) {
QTextLine line = textLayout->createLine();
if (!line.isValid())
break;
line.setLineWidth(lineWidth);
line.setPosition(QPointF(0, height));
height += line.height();
widthUsed = qMax(widthUsed, line.naturalTextWidth());
}
textLayout->endLayout();
return QSizeF(widthUsed, height);
}
// copied from QItemDelegate to be able to add the 'format' parameter
void HighlightingItemDelegate::drawDisplay(QPainter *painter,
const QStyleOptionViewItem &option,
const QRect &rect, const QString &text,
const QVector<QTextLayout::FormatRange> &format) const
{
QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
? QPalette::Normal : QPalette::Disabled;
if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
cg = QPalette::Inactive;
if (option.state & QStyle::State_Selected) {
painter->fillRect(rect, option.palette.brush(cg, QPalette::Highlight));
painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
} else {
painter->setPen(option.palette.color(cg, QPalette::Text));
}
if (text.isEmpty())
return;
if (option.state & QStyle::State_Editing) {
painter->save();
painter->setPen(option.palette.color(cg, QPalette::Text));
painter->drawRect(rect.adjusted(0, 0, -1, -1));
painter->restore();
}
const QStyleOptionViewItem opt = option;
const QWidget *widget = option.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
const bool wrapText = opt.features & QStyleOptionViewItem::WrapText;
QTextOption textOption;
textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
textOption.setTextDirection(option.direction);
textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
QTextLayout textLayout;
textLayout.setTextOption(textOption);
textLayout.setFont(option.font);
textLayout.setText(replaceNewLine(text));
QSizeF textLayoutSize = doTextLayout(&textLayout, textRect.width());
if (textRect.width() < textLayoutSize.width()
|| textRect.height() < textLayoutSize.height()) {
QString elided;
int start = 0;
int end = text.indexOf(QChar::LineSeparator, start);
if (end == -1) {
elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
} else {
while (end != -1) {
elided += option.fontMetrics.elidedText(text.mid(start, end - start),
option.textElideMode, textRect.width());
elided += QChar::LineSeparator;
start = end + 1;
end = text.indexOf(QChar::LineSeparator, start);
}
// let's add the last line (after the last QChar::LineSeparator)
elided += option.fontMetrics.elidedText(text.mid(start),
option.textElideMode, textRect.width());
}
textLayout.setText(elided);
textLayoutSize = doTextLayout(&textLayout, textRect.width());
}
const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
layoutSize, textRect);
// if we still overflow even after eliding the text, enable clipping
if (!hasClipping() && (textRect.width() < textLayoutSize.width()
|| textRect.height() < textLayoutSize.height())) {
painter->save();
painter->setClipRect(layoutRect);
textLayout.draw(painter, layoutRect.topLeft(), format, layoutRect);
painter->restore();
} else {
textLayout.draw(painter, layoutRect.topLeft(), format, layoutRect);
}
}
} // namespace Utils