ClangTools: Move check box in diagnostic view to the left

...so it's closer to other related data and controls (expand/collapse
marker).

The details of the fixits status are displayed in the tooltip now.

Change-Id: I9f1a9e9562572195b52a097ae9278647fecf6cb8
Reviewed-by: Ivan Donchevskii <ivan.donchevskii@qt.io>
This commit is contained in:
Nikolai Kosjar
2019-02-11 13:06:05 +01:00
parent d4415b02af
commit 4eaedac72f
4 changed files with 177 additions and 83 deletions

View File

@@ -84,7 +84,7 @@ ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent)
: Utils::TreeModel<>(parent)
, m_filesWatcher(std::make_unique<QFileSystemWatcher>())
{
setHeader({tr("Diagnostic"), tr("Fixit Status")});
setHeader({tr("Diagnostic")});
connectFileWatcher();
}
@@ -192,7 +192,26 @@ void ClangToolsDiagnosticModel::addWatchedPath(const QString &path)
m_filesWatcher->addPath(path);
}
static QString createDiagnosticToolTipString(const Diagnostic &diagnostic)
static QString fixitStatus(FixitStatus status)
{
switch (status) {
case FixitStatus::NotAvailable:
return ClangToolsDiagnosticModel::tr("No Fixits");
case FixitStatus::NotScheduled:
return ClangToolsDiagnosticModel::tr("Not Scheduled");
case FixitStatus::Invalidated:
return ClangToolsDiagnosticModel::tr("Invalidated");
case FixitStatus::Scheduled:
return ClangToolsDiagnosticModel::tr("Scheduled");
case FixitStatus::FailedToApply:
return ClangToolsDiagnosticModel::tr("Failed to Apply");
case FixitStatus::Applied:
return ClangToolsDiagnosticModel::tr("Applied");
}
return QString();
}
static QString createDiagnosticToolTipString(const Diagnostic &diagnostic, FixitStatus fixItStatus)
{
using StringPair = QPair<QString, QString>;
QList<StringPair> lines;
@@ -226,6 +245,10 @@ static QString createDiagnosticToolTipString(const Diagnostic &diagnostic)
QCoreApplication::translate("ClangTools::Diagnostic", "Location:"),
createFullLocationString(diagnostic.location));
lines << qMakePair(
QCoreApplication::translate("ClangTools::Diagnostic", "Fixit Status:"),
fixitStatus(fixItStatus));
QString html = QLatin1String("<html>"
"<head>"
"<style>dt { font-weight:bold; } dd { font-family: monospace; }</style>\n"
@@ -364,19 +387,8 @@ DiagnosticItem::~DiagnosticItem()
Qt::ItemFlags DiagnosticItem::flags(int column) const
{
const Qt::ItemFlags itemFlags = TreeItem::flags(column);
if (column == DiagnosticView::FixItColumn) {
switch (m_fixitStatus) {
case FixitStatus::NotAvailable:
case FixitStatus::Applied:
case FixitStatus::FailedToApply:
case FixitStatus::Invalidated:
return itemFlags & ~Qt::ItemIsEnabled;
case FixitStatus::Scheduled:
case FixitStatus::NotScheduled:
if (column == DiagnosticView::DiagnosticColumn)
return itemFlags | Qt::ItemIsUserCheckable;
}
}
return itemFlags;
}
@@ -395,35 +407,7 @@ static QVariant iconData(const QString &type)
QVariant DiagnosticItem::data(int column, int role) const
{
if (column == DiagnosticView::FixItColumn) {
if (role == Qt::CheckStateRole) {
switch (m_fixitStatus) {
case FixitStatus::NotAvailable:
case FixitStatus::NotScheduled:
case FixitStatus::Invalidated:
case FixitStatus::Applied:
case FixitStatus::FailedToApply:
return Qt::Unchecked;
case FixitStatus::Scheduled:
return Qt::Checked;
}
} else if (role == Qt::DisplayRole) {
switch (m_fixitStatus) {
case FixitStatus::NotAvailable:
return ClangToolsDiagnosticModel::tr("No Fixits");
case FixitStatus::NotScheduled:
return ClangToolsDiagnosticModel::tr("Not Scheduled");
case FixitStatus::Invalidated:
return ClangToolsDiagnosticModel::tr("Invalidated");
case FixitStatus::Scheduled:
return ClangToolsDiagnosticModel::tr("Scheduled");
case FixitStatus::FailedToApply:
return ClangToolsDiagnosticModel::tr("Failed to Apply");
case FixitStatus::Applied:
return ClangToolsDiagnosticModel::tr("Applied");
}
}
} else if (column == DiagnosticView::DiagnosticColumn) {
if (column == DiagnosticView::DiagnosticColumn) {
switch (role) {
case Debugger::DetailedErrorView::LocationRole:
return QVariant::fromValue(m_diagnostic.location);
@@ -433,11 +417,34 @@ QVariant DiagnosticItem::data(int column, int role) const
return QVariant::fromValue(m_diagnostic);
case ClangToolsDiagnosticModel::TextRole:
return m_diagnostic.description;
case ClangToolsDiagnosticModel::CheckBoxEnabledRole:
switch (m_fixitStatus) {
case FixitStatus::NotAvailable:
case FixitStatus::Applied:
case FixitStatus::FailedToApply:
case FixitStatus::Invalidated:
return false;
case FixitStatus::Scheduled:
case FixitStatus::NotScheduled:
return true;
}
case Qt::CheckStateRole: {
switch (m_fixitStatus) {
case FixitStatus::NotAvailable:
case FixitStatus::Invalidated:
case FixitStatus::Applied:
case FixitStatus::FailedToApply:
case FixitStatus::NotScheduled:
return Qt::Unchecked;
case FixitStatus::Scheduled:
return Qt::Checked;
}
}
case Qt::DisplayRole:
return QString("%1: %2").arg(lineColumnString(m_diagnostic.location),
m_diagnostic.description);
case Qt::ToolTipRole:
return createDiagnosticToolTipString(m_diagnostic);
return createDiagnosticToolTipString(m_diagnostic, m_fixitStatus);
case Qt::DecorationRole:
return iconData(m_diagnostic.type);
default:
@@ -450,7 +457,7 @@ QVariant DiagnosticItem::data(int column, int role) const
bool DiagnosticItem::setData(int column, const QVariant &data, int role)
{
if (column == DiagnosticView::FixItColumn && role == Qt::CheckStateRole) {
if (column == DiagnosticView::DiagnosticColumn && role == Qt::CheckStateRole) {
if (m_fixitStatus != FixitStatus::Scheduled && m_fixitStatus != FixitStatus::NotScheduled)
return false;
@@ -510,9 +517,6 @@ static QString rangeString(const QVector<Debugger::DiagnosticLocation> &ranges)
QVariant ExplainingStepItem::data(int column, int role) const
{
if (column == DiagnosticView::FixItColumn)
return QVariant();
if (column == DiagnosticView::DiagnosticColumn) {
// DiagnosticColumn
switch (role) {

View File

@@ -112,7 +112,11 @@ public:
void addDiagnostics(const QList<Diagnostic> &diagnostics);
QSet<Diagnostic> diagnostics() const;
enum ItemRole { DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1, TextRole };
enum ItemRole {
DiagnosticRole = Debugger::DetailedErrorView::FullTextRole + 1,
TextRole,
CheckBoxEnabledRole
};
void clear();
void removeWatchedPath(const QString &path);

View File

@@ -30,11 +30,13 @@
#include "clangtoolsutils.h"
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/manhattanstyle.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <QAction>
#include <QApplication>
#include <QDebug>
#include <QHeaderView>
#include <QPainter>
@@ -44,21 +46,22 @@ using namespace Debugger;
namespace ClangTools {
namespace Internal {
class ClickableFixItHeader : public QHeaderView
// A header view that puts a check box in front of a given column.
class HeaderWithCheckBoxInColumn : public QHeaderView
{
Q_OBJECT
public:
ClickableFixItHeader(Qt::Orientation orientation, QWidget *parent = nullptr)
HeaderWithCheckBoxInColumn(Qt::Orientation orientation,
int column = 0,
QWidget *parent = nullptr)
: QHeaderView(orientation, parent)
, m_column(column)
{
setDefaultAlignment(Qt::AlignLeft);
}
void setState(QFlags<QStyle::StateFlag> newState)
{
state = newState;
}
void setState(QFlags<QStyle::StateFlag> newState) { state = newState; }
protected:
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override
@@ -66,7 +69,7 @@ protected:
painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore();
if (logicalIndex == DiagnosticView::FixItColumn) {
if (logicalIndex == m_column) {
QStyleOptionButton option;
const int side = sizeHint().height();
option.rect = QRect(rect.left() + 1, 1, side - 3, side - 3);
@@ -83,32 +86,113 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override
{
const int x = event->localPos().x();
const int fixItColumnX = sectionPosition(DiagnosticView::FixItColumn);
const bool isWithinFixitCheckBox = x > fixItColumnX
&& x < fixItColumnX + sizeHint().height() - 3;
if (isWithinFixitCheckBox) {
const int columnX = sectionPosition(m_column);
const bool isWithinCheckBox = x > columnX && x < columnX + sizeHint().height() - 3;
if (isWithinCheckBox) {
state = (state != QStyle::State_On) ? QStyle::State_On : QStyle::State_Off;
viewport()->update();
emit fixItColumnClicked(state == QStyle::State_On);
emit checkBoxClicked(state == QStyle::State_On);
return; // Avoid changing sort order
}
QHeaderView::mouseReleaseEvent(event);
}
signals:
void fixItColumnClicked(bool checked);
void checkBoxClicked(bool checked);
private:
const int m_column = 0;
QFlags<QStyle::StateFlag> state = QStyle::State_Off;
};
static QString getBaseStyleName()
{
QStyle *style = QApplication::style();
if (auto proxyStyle = qobject_cast<QProxyStyle *>(style))
style = proxyStyle->baseStyle();
return style->objectName();
}
// Paints the check box indicator disabled if requested by client.
class DiagnosticViewStyle : public ManhattanStyle
{
public:
DiagnosticViewStyle(const QString &baseStyleName = getBaseStyleName())
: ManhattanStyle(baseStyleName)
{}
void setPaintCheckBoxDisabled(bool paintDisabledCheckbox)
{
m_paintCheckBoxDisabled = paintDisabledCheckbox;
}
void drawPrimitive(PrimitiveElement element,
const QStyleOption *option,
QPainter *painter,
const QWidget *widget = nullptr) const final
{
if (element == QStyle::PE_IndicatorCheckBox && m_paintCheckBoxDisabled) {
if (const QStyleOptionButton *o = qstyleoption_cast<const QStyleOptionButton *>(
option)) {
QStyleOptionButton myOption = *o;
myOption.palette.setCurrentColorGroup(QPalette::Disabled);
ManhattanStyle::drawPrimitive(element, &myOption, painter, widget);
return;
}
}
ManhattanStyle::drawPrimitive(element, option, painter, widget);
}
private:
bool m_paintCheckBoxDisabled = false;
};
// A delegate that allows to paint a disabled check box (indicator).
// This is useful if the rest of the item should not be disabled.
class DiagnosticViewDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
DiagnosticViewDelegate(DiagnosticViewStyle *style)
: m_style(style)
{}
void paint(QPainter *painter,
const QStyleOptionViewItem &option,
const QModelIndex &index) const final
{
const bool paintDisabled = !index.data(ClangToolsDiagnosticModel::CheckBoxEnabledRole)
.toBool();
if (paintDisabled)
m_style->setPaintCheckBoxDisabled(true);
QStyledItemDelegate::paint(painter, option, index);
if (paintDisabled)
m_style->setPaintCheckBoxDisabled(false);
}
private:
DiagnosticViewStyle *m_style = nullptr;
};
DiagnosticView::DiagnosticView(QWidget *parent)
: Debugger::DetailedErrorView(parent)
, m_style(new DiagnosticViewStyle)
, m_delegate(new DiagnosticViewDelegate(m_style))
{
m_suppressAction = new QAction(tr("Suppress This Diagnostic"), this);
connect(m_suppressAction, &QAction::triggered,
this, &DiagnosticView::suppressCurrentDiagnostic);
installEventFilter(this);
setStyle(m_style);
setItemDelegate(m_delegate);
}
DiagnosticView::~DiagnosticView()
{
delete m_delegate;
delete m_style;
}
void DiagnosticView::suppressCurrentDiagnostic()
@@ -219,11 +303,11 @@ void DiagnosticView::setSelectedFixItsCount(int fixItsCount)
{
if (m_ignoreSetSelectedFixItsCount)
return;
auto *clickableFixItHeader = static_cast<ClickableFixItHeader *>(header());
clickableFixItHeader->setState(fixItsCount
auto checkBoxHeader = static_cast<HeaderWithCheckBoxInColumn *>(header());
checkBoxHeader->setState(fixItsCount
? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off)
: QStyle::State_Off);
clickableFixItHeader->viewport()->update();
checkBoxHeader->viewport()->update();
}
void DiagnosticView::openEditorForCurrentIndex()
@@ -240,25 +324,25 @@ void DiagnosticView::setModel(QAbstractItemModel *theProxyModel)
const auto sourceModel = static_cast<ClangToolsDiagnosticModel *>(proxyModel->sourceModel());
Debugger::DetailedErrorView::setModel(proxyModel);
auto *clickableFixItHeader = new ClickableFixItHeader(Qt::Horizontal, this);
connect(clickableFixItHeader, &ClickableFixItHeader::fixItColumnClicked, this, [=](bool checked) {
auto *header = new HeaderWithCheckBoxInColumn(Qt::Horizontal,
DiagnosticView::DiagnosticColumn,
this);
connect(header, &HeaderWithCheckBoxInColumn::checkBoxClicked, this, [=](bool checked) {
m_ignoreSetSelectedFixItsCount = true;
sourceModel->rootItem()->forChildrenAtLevel(2, [&](::Utils::TreeItem *item) {
auto diagnosticItem = static_cast<DiagnosticItem *>(item);
diagnosticItem->setData(FixItColumn,
diagnosticItem->setData(DiagnosticView::DiagnosticColumn,
checked ? Qt::Checked : Qt::Unchecked,
Qt::CheckStateRole);
});
m_ignoreSetSelectedFixItsCount = false;
});
setHeader(clickableFixItHeader);
clickableFixItHeader->setStretchLastSection(false);
clickableFixItHeader->setSectionResizeMode(0, QHeaderView::Stretch);
clickableFixItHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents);
const int fixitColumnWidth = clickableFixItHeader->sectionSizeHint(DiagnosticView::FixItColumn);
const int checkboxWidth = clickableFixItHeader->height();
clickableFixItHeader->setMinimumSectionSize(fixitColumnWidth + 1.2 * checkboxWidth);
setHeader(header);
header->setSectionResizeMode(DiagnosticView::DiagnosticColumn, QHeaderView::Stretch);
const int columnWidth = header->sectionSizeHint(DiagnosticView::DiagnosticColumn);
const int checkboxWidth = header->height();
header->setMinimumSectionSize(columnWidth + 1.2 * checkboxWidth);
}
} // namespace Internal

View File

@@ -30,16 +30,16 @@
namespace ClangTools {
namespace Internal {
class DiagnosticViewStyle;
class DiagnosticViewDelegate;
class DiagnosticView : public Debugger::DetailedErrorView
{
Q_OBJECT
public:
DiagnosticView(QWidget *parent = nullptr);
enum ExtraColumn {
FixItColumn = DiagnosticColumn + 1,
};
~DiagnosticView() override;
void setSelectedFixItsCount(int fixItsCount);
@@ -59,6 +59,8 @@ private:
void setModel(QAbstractItemModel *theProxyModel) override;
QAction *m_suppressAction;
DiagnosticViewStyle *m_style = nullptr;
DiagnosticViewDelegate *m_delegate = nullptr;
bool m_ignoreSetSelectedFixItsCount = false;
};