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

View File

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

View File

@@ -30,11 +30,13 @@
#include "clangtoolsutils.h" #include "clangtoolsutils.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/manhattanstyle.h>
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QAction> #include <QAction>
#include <QApplication>
#include <QDebug> #include <QDebug>
#include <QHeaderView> #include <QHeaderView>
#include <QPainter> #include <QPainter>
@@ -44,21 +46,22 @@ using namespace Debugger;
namespace ClangTools { namespace ClangTools {
namespace Internal { 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 Q_OBJECT
public: public:
ClickableFixItHeader(Qt::Orientation orientation, QWidget *parent = nullptr) HeaderWithCheckBoxInColumn(Qt::Orientation orientation,
int column = 0,
QWidget *parent = nullptr)
: QHeaderView(orientation, parent) : QHeaderView(orientation, parent)
, m_column(column)
{ {
setDefaultAlignment(Qt::AlignLeft); setDefaultAlignment(Qt::AlignLeft);
} }
void setState(QFlags<QStyle::StateFlag> newState) void setState(QFlags<QStyle::StateFlag> newState) { state = newState; }
{
state = newState;
}
protected: protected:
void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const override
@@ -66,7 +69,7 @@ protected:
painter->save(); painter->save();
QHeaderView::paintSection(painter, rect, logicalIndex); QHeaderView::paintSection(painter, rect, logicalIndex);
painter->restore(); painter->restore();
if (logicalIndex == DiagnosticView::FixItColumn) { if (logicalIndex == m_column) {
QStyleOptionButton option; QStyleOptionButton option;
const int side = sizeHint().height(); const int side = sizeHint().height();
option.rect = QRect(rect.left() + 1, 1, side - 3, side - 3); option.rect = QRect(rect.left() + 1, 1, side - 3, side - 3);
@@ -83,32 +86,113 @@ protected:
void mouseReleaseEvent(QMouseEvent *event) override void mouseReleaseEvent(QMouseEvent *event) override
{ {
const int x = event->localPos().x(); const int x = event->localPos().x();
const int fixItColumnX = sectionPosition(DiagnosticView::FixItColumn); const int columnX = sectionPosition(m_column);
const bool isWithinFixitCheckBox = x > fixItColumnX const bool isWithinCheckBox = x > columnX && x < columnX + sizeHint().height() - 3;
&& x < fixItColumnX + sizeHint().height() - 3; if (isWithinCheckBox) {
if (isWithinFixitCheckBox) {
state = (state != QStyle::State_On) ? QStyle::State_On : QStyle::State_Off; state = (state != QStyle::State_On) ? QStyle::State_On : QStyle::State_Off;
viewport()->update(); viewport()->update();
emit fixItColumnClicked(state == QStyle::State_On); emit checkBoxClicked(state == QStyle::State_On);
return; // Avoid changing sort order return; // Avoid changing sort order
} }
QHeaderView::mouseReleaseEvent(event); QHeaderView::mouseReleaseEvent(event);
} }
signals: signals:
void fixItColumnClicked(bool checked); void checkBoxClicked(bool checked);
private: private:
const int m_column = 0;
QFlags<QStyle::StateFlag> state = QStyle::State_Off; 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) DiagnosticView::DiagnosticView(QWidget *parent)
: Debugger::DetailedErrorView(parent) : Debugger::DetailedErrorView(parent)
, m_style(new DiagnosticViewStyle)
, m_delegate(new DiagnosticViewDelegate(m_style))
{ {
m_suppressAction = new QAction(tr("Suppress This Diagnostic"), this); m_suppressAction = new QAction(tr("Suppress This Diagnostic"), this);
connect(m_suppressAction, &QAction::triggered, connect(m_suppressAction, &QAction::triggered,
this, &DiagnosticView::suppressCurrentDiagnostic); this, &DiagnosticView::suppressCurrentDiagnostic);
installEventFilter(this); installEventFilter(this);
setStyle(m_style);
setItemDelegate(m_delegate);
}
DiagnosticView::~DiagnosticView()
{
delete m_delegate;
delete m_style;
} }
void DiagnosticView::suppressCurrentDiagnostic() void DiagnosticView::suppressCurrentDiagnostic()
@@ -219,11 +303,11 @@ void DiagnosticView::setSelectedFixItsCount(int fixItsCount)
{ {
if (m_ignoreSetSelectedFixItsCount) if (m_ignoreSetSelectedFixItsCount)
return; return;
auto *clickableFixItHeader = static_cast<ClickableFixItHeader *>(header()); auto checkBoxHeader = static_cast<HeaderWithCheckBoxInColumn *>(header());
clickableFixItHeader->setState(fixItsCount checkBoxHeader->setState(fixItsCount
? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off) ? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off)
: QStyle::State_Off); : QStyle::State_Off);
clickableFixItHeader->viewport()->update(); checkBoxHeader->viewport()->update();
} }
void DiagnosticView::openEditorForCurrentIndex() void DiagnosticView::openEditorForCurrentIndex()
@@ -240,25 +324,25 @@ void DiagnosticView::setModel(QAbstractItemModel *theProxyModel)
const auto sourceModel = static_cast<ClangToolsDiagnosticModel *>(proxyModel->sourceModel()); const auto sourceModel = static_cast<ClangToolsDiagnosticModel *>(proxyModel->sourceModel());
Debugger::DetailedErrorView::setModel(proxyModel); 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; m_ignoreSetSelectedFixItsCount = true;
sourceModel->rootItem()->forChildrenAtLevel(2, [&](::Utils::TreeItem *item) { sourceModel->rootItem()->forChildrenAtLevel(2, [&](::Utils::TreeItem *item) {
auto diagnosticItem = static_cast<DiagnosticItem *>(item); auto diagnosticItem = static_cast<DiagnosticItem *>(item);
diagnosticItem->setData(FixItColumn, diagnosticItem->setData(DiagnosticView::DiagnosticColumn,
checked ? Qt::Checked : Qt::Unchecked, checked ? Qt::Checked : Qt::Unchecked,
Qt::CheckStateRole); Qt::CheckStateRole);
}); });
m_ignoreSetSelectedFixItsCount = false; m_ignoreSetSelectedFixItsCount = false;
}); });
setHeader(clickableFixItHeader); setHeader(header);
clickableFixItHeader->setStretchLastSection(false); header->setSectionResizeMode(DiagnosticView::DiagnosticColumn, QHeaderView::Stretch);
clickableFixItHeader->setSectionResizeMode(0, QHeaderView::Stretch); const int columnWidth = header->sectionSizeHint(DiagnosticView::DiagnosticColumn);
clickableFixItHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); const int checkboxWidth = header->height();
header->setMinimumSectionSize(columnWidth + 1.2 * checkboxWidth);
const int fixitColumnWidth = clickableFixItHeader->sectionSizeHint(DiagnosticView::FixItColumn);
const int checkboxWidth = clickableFixItHeader->height();
clickableFixItHeader->setMinimumSectionSize(fixitColumnWidth + 1.2 * checkboxWidth);
} }
} // namespace Internal } // namespace Internal

View File

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