Index: plugins/problemreporter/problemtreeview.cpp =================================================================== --- plugins/problemreporter/problemtreeview.cpp +++ plugins/problemreporter/problemtreeview.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include @@ -56,34 +58,101 @@ Q_OBJECT public: - explicit ProblemTreeViewItemDelegate(QObject* parent = nullptr); + explicit ProblemTreeViewItemDelegate(ProblemTreeView* parent = nullptr); void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + +public Q_SLOTS: + void headerDataChanged(int index, int oldSize, int newSize); + +Q_SIGNALS: + void errorColumnPainted() const; +private Q_SLOTS: + void monitorHeaderChange(); + +public: + ProblemTreeView* m_view; + bool m_headerChanged; + bool m_headerChangeMonitored; }; } -ProblemTreeViewItemDelegate::ProblemTreeViewItemDelegate(QObject* parent) +ProblemTreeViewItemDelegate::ProblemTreeViewItemDelegate(ProblemTreeView* parent) : QItemDelegate(parent) + , m_view(parent) + , m_headerChanged(false) + , m_headerChangeMonitored(false) { + connect(this, &ProblemTreeViewItemDelegate::errorColumnPainted, this, &ProblemTreeViewItemDelegate::monitorHeaderChange); } void ProblemTreeViewItemDelegate::paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const { QStyleOptionViewItem newOption(option); - newOption.textElideMode = index.column() == ProblemModel::File ? Qt::ElideMiddle : Qt::ElideRight; + if (index.column() == ProblemModel::Error) { + newOption.features |= QStyleOptionViewItem::WrapText; + newOption.textElideMode = Qt::ElideNone; + emit errorColumnPainted(); + } else { + newOption.features &= ~QStyleOptionViewItem::WrapText; + newOption.textElideMode = index.column() == ProblemModel::File ? Qt::ElideMiddle : Qt::ElideRight; + } QItemDelegate::paint(painter, newOption, index); } +QSize ProblemTreeViewItemDelegate::sizeHint(const QStyleOptionViewItem& option, + const QModelIndex& index) const +{ + QSize ret = QItemDelegate::sizeHint(option, index); + auto viewWidth = m_view->width(); + if (index.column() == ProblemModel::Error) { + const auto text = index.model()->data(index).toString(); + auto width = ret.width(); + // allocate 2/3 of the view width to the (error) message, the rest to line number, file etc. + // TODO: get this width from the current error column width iff the header was resized by the user, + // with 2/3 as the initial/default + auto errorWidth = m_headerChanged ? m_view->header()->sectionSize(ProblemModel::Error) : viewWidth * 2 / 3; + if (width > errorWidth) { + auto doc = new QTextDocument(text); + QTextOption textOption; + textOption.setWrapMode(QTextOption::WordWrap); + doc->setDefaultTextOption(textOption); + ret.setWidth(errorWidth); + // this is where wrapping is likely to kick in + doc->setTextWidth(ret.width()); + ret.setHeight(qCeil(doc->size().height())); + delete doc; + } + } + return ret; +} + +void ProblemTreeViewItemDelegate::headerDataChanged(int index, int, int) +{ + if (index == ProblemModel::Error && m_headerChangeMonitored) { + m_headerChanged = true; + } +} + +void ProblemTreeViewItemDelegate::monitorHeaderChange() +{ + m_headerChangeMonitored = true; +} + ProblemTreeView::ProblemTreeView(QWidget* parent, QAbstractItemModel* itemModel) : QTreeView(parent) , m_proxy(new QSortFilterProxyModel(this)) { setObjectName(QStringLiteral("Problem Reporter Tree")); setWhatsThis(i18n("Problems")); - setItemDelegate(new ProblemTreeViewItemDelegate(this)); + auto itemDelegate = new ProblemTreeViewItemDelegate(this); + setItemDelegate(itemDelegate); setSelectionBehavior(QAbstractItemView::SelectRows); + setWordWrap(false); + setUniformRowHeights(false); m_proxy->setSortRole(ProblemModel::SeverityRole); m_proxy->setDynamicSortFilter(true); @@ -103,6 +172,7 @@ connect(model(), &QAbstractItemModel::rowsInserted, this, &ProblemTreeView::changed); connect(model(), &QAbstractItemModel::rowsRemoved, this, &ProblemTreeView::changed); connect(model(), &QAbstractItemModel::modelReset, this, &ProblemTreeView::changed); + connect(header(), &QHeaderView::sectionResized, itemDelegate, &ProblemTreeViewItemDelegate::headerDataChanged); m_proxy->setFilterKeyColumn(-1); m_proxy->setFilterCaseSensitivity(Qt::CaseInsensitive); @@ -143,8 +213,11 @@ void ProblemTreeView::resizeColumns() { - for (int i = 0; i < model()->columnCount(); ++i) - resizeColumnToContents(i); + for (int i = 0; i < model()->columnCount(); ++i) { + if (i != ProblemModel::Error || !qobject_cast(itemDelegate())->m_headerChanged) { + resizeColumnToContents(i); + } + } } void ProblemTreeView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight, const QVector& roles) @@ -157,6 +230,9 @@ { QTreeView::reset(); resizeColumns(); + const auto delegate = qobject_cast(itemDelegate()); + delegate->m_headerChanged = false; + delegate->m_headerChangeMonitored = false; } int ProblemTreeView::setFilter(const QString& filterText)