diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp index 88b80965181..0614bce37db 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.cpp @@ -84,7 +84,7 @@ ClangToolsDiagnosticModel::ClangToolsDiagnosticModel(QObject *parent) : Utils::TreeModel<>(parent) , m_filesWatcher(std::make_unique()) { - 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; QList 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("" "" "\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: - return itemFlags | Qt::ItemIsUserCheckable; - } - } - + 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 &ranges) QVariant ExplainingStepItem::data(int column, int role) const { - if (column == DiagnosticView::FixItColumn) - return QVariant(); - if (column == DiagnosticView::DiagnosticColumn) { // DiagnosticColumn switch (role) { diff --git a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h index 22d06992018..a8c4dcd011a 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticmodel.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticmodel.h @@ -112,7 +112,11 @@ public: void addDiagnostics(const QList &diagnostics); QSet 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); diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp index 07bc7f397f4..54cea0c7766 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.cpp +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.cpp @@ -30,11 +30,13 @@ #include "clangtoolsutils.h" #include +#include #include #include #include +#include #include #include #include @@ -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 newState) - { - state = newState; - } + void setState(QFlags 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 state = QStyle::State_Off; }; +static QString getBaseStyleName() +{ + QStyle *style = QApplication::style(); + if (auto proxyStyle = qobject_cast(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( + 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(header()); - clickableFixItHeader->setState(fixItsCount - ? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off) - : QStyle::State_Off); - clickableFixItHeader->viewport()->update(); + auto checkBoxHeader = static_cast(header()); + checkBoxHeader->setState(fixItsCount + ? (QStyle::State_NoChange | QStyle::State_On | QStyle::State_Off) + : QStyle::State_Off); + checkBoxHeader->viewport()->update(); } void DiagnosticView::openEditorForCurrentIndex() @@ -240,25 +324,25 @@ void DiagnosticView::setModel(QAbstractItemModel *theProxyModel) const auto sourceModel = static_cast(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(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 diff --git a/src/plugins/clangtools/clangtoolsdiagnosticview.h b/src/plugins/clangtools/clangtoolsdiagnosticview.h index 7cb287ab391..8580b93b734 100644 --- a/src/plugins/clangtools/clangtoolsdiagnosticview.h +++ b/src/plugins/clangtools/clangtoolsdiagnosticview.h @@ -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; };