forked from qt-creator/qt-creator
Advanced Search: Add horizontal scroll bar
Compute the actual size hint for the search results and make the column resize to its contents. To make the highlight of selected items still span the whole widget, we force the column to span at least the width of the tree view, by setting the minimum section size accordingly on resize events. Fixes: QTCREATORBUG-21099 Change-Id: Ia9e88fba8185429fede8a82402a62285afb1e0dd Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -40,59 +40,101 @@ SearchResultTreeItemDelegate::SearchResultTreeItemDelegate(int tabWidth, QObject
|
|||||||
setTabWidth(tabWidth);
|
setTabWidth(tabWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
const int lineNumberAreaHorizontalPadding = 4;
|
||||||
|
const int minimumLineNumberDigits = 6;
|
||||||
|
|
||||||
|
static std::pair<int, QString> lineNumberInfo(const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index)
|
||||||
|
{
|
||||||
|
const int lineNumber = index.data(ItemDataRoles::ResultBeginLineNumberRole).toInt();
|
||||||
|
if (lineNumber < 1)
|
||||||
|
return {0, {}};
|
||||||
|
const QString lineNumberText = QString::number(lineNumber);
|
||||||
|
const int lineNumberDigits = qMax(minimumLineNumberDigits, lineNumberText.count());
|
||||||
|
const int fontWidth = option.fontMetrics.horizontalAdvance(QString(lineNumberDigits, QLatin1Char('0')));
|
||||||
|
const QStyle *style = option.widget ? option.widget->style() : QApplication::style();
|
||||||
|
return {lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding
|
||||||
|
+ style->pixelMetric(QStyle::PM_FocusFrameHMargin),
|
||||||
|
lineNumberText};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString itemText(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
const QString text = index.data(Qt::DisplayRole).toString();
|
||||||
|
// show number of subresults in displayString
|
||||||
|
if (index.model()->hasChildren(index)) {
|
||||||
|
return text + QLatin1String(" (") + QString::number(index.model()->rowCount(index))
|
||||||
|
+ QLatin1Char(')');
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
LayoutInfo SearchResultTreeItemDelegate::getLayoutInfo(const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
static const int iconSize = 16;
|
static const int iconSize = 16;
|
||||||
|
|
||||||
painter->save();
|
LayoutInfo info;
|
||||||
|
info.option = setOptions(index, option);
|
||||||
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
|
// check mark
|
||||||
bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable);
|
const bool checkable = (index.model()->flags(index) & Qt::ItemIsUserCheckable);
|
||||||
Qt::CheckState checkState = Qt::Unchecked;
|
info.checkState = Qt::Unchecked;
|
||||||
if (checkable) {
|
if (checkable) {
|
||||||
QVariant checkStateData = index.data(Qt::CheckStateRole);
|
QVariant checkStateData = index.data(Qt::CheckStateRole);
|
||||||
checkState = static_cast<Qt::CheckState>(checkStateData.toInt());
|
info.checkState = static_cast<Qt::CheckState>(checkStateData.toInt());
|
||||||
checkRect = doCheck(opt, opt.rect, checkStateData);
|
info.checkRect = doCheck(info.option, info.option.rect, checkStateData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// icon
|
// icon
|
||||||
QIcon icon = index.model()->data(index, ItemDataRoles::ResultIconRole).value<QIcon>();
|
info.icon = index.data(ItemDataRoles::ResultIconRole).value<QIcon>();
|
||||||
if (!icon.isNull()) {
|
if (!info.icon.isNull()) {
|
||||||
const QSize size = icon.actualSize(QSize(iconSize, iconSize));
|
const QSize size = info.icon.actualSize(QSize(iconSize, iconSize));
|
||||||
pixmapRect = QRect(0, 0, size.width(), size.height());
|
info.pixmapRect = QRect(0, 0, size.width(), size.height());
|
||||||
}
|
}
|
||||||
|
|
||||||
// text
|
// text
|
||||||
textRect = opt.rect.adjusted(0, 0, checkRect.width() + pixmapRect.width(), 0);
|
info.textRect = info.option.rect.adjusted(0,
|
||||||
|
0,
|
||||||
|
info.checkRect.width() + info.pixmapRect.width(),
|
||||||
|
0);
|
||||||
|
|
||||||
|
// do basic layout
|
||||||
|
doLayout(info.option, &info.checkRect, &info.pixmapRect, &info.textRect, false);
|
||||||
|
|
||||||
|
// adapt for line numbers
|
||||||
|
const int lineNumberWidth = lineNumberInfo(info.option, index).first;
|
||||||
|
info.lineNumberRect = info.textRect;
|
||||||
|
info.lineNumberRect.setWidth(lineNumberWidth);
|
||||||
|
info.textRect.adjust(lineNumberWidth, 0, 0, 0);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SearchResultTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
painter->save();
|
||||||
|
|
||||||
|
const LayoutInfo info = getLayoutInfo(option, index);
|
||||||
|
|
||||||
|
painter->setFont(info.option.font);
|
||||||
|
|
||||||
|
QItemDelegate::drawBackground(painter, info.option, index);
|
||||||
|
|
||||||
// do layout
|
|
||||||
doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
|
|
||||||
// ---- draw the items
|
// ---- draw the items
|
||||||
// icon
|
// icon
|
||||||
if (!icon.isNull())
|
if (!info.icon.isNull())
|
||||||
icon.paint(painter, pixmapRect, option.decorationAlignment);
|
info.icon.paint(painter, info.pixmapRect, info.option.decorationAlignment);
|
||||||
|
|
||||||
// line numbers
|
// line numbers
|
||||||
int lineNumberAreaWidth = drawLineNumber(painter, opt, textRect, index);
|
drawLineNumber(painter, info.option, info.lineNumberRect, index);
|
||||||
textRect.adjust(lineNumberAreaWidth, 0, 0, 0);
|
|
||||||
|
|
||||||
// text and focus/selection
|
// text and focus/selection
|
||||||
drawText(painter, opt, textRect, index);
|
drawText(painter, info.option, info.textRect, index);
|
||||||
QItemDelegate::drawFocus(painter, opt, opt.rect);
|
QItemDelegate::drawFocus(painter, info.option, info.option.rect);
|
||||||
|
|
||||||
// check mark
|
// check mark
|
||||||
if (checkable)
|
if (info.checkRect.isValid())
|
||||||
QItemDelegate::drawCheck(painter, opt, checkRect, checkState);
|
QItemDelegate::drawCheck(painter, info.option, info.checkRect, info.checkState);
|
||||||
|
|
||||||
painter->restore();
|
painter->restore();
|
||||||
}
|
}
|
||||||
@@ -102,22 +144,31 @@ void SearchResultTreeItemDelegate::setTabWidth(int width)
|
|||||||
m_tabString = QString(width, QLatin1Char(' '));
|
m_tabString = QString(width, QLatin1Char(' '));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSize SearchResultTreeItemDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||||
|
const QModelIndex &index) const
|
||||||
|
{
|
||||||
|
const LayoutInfo info = getLayoutInfo(option, index);
|
||||||
|
const int height = index.data(Qt::SizeHintRole).value<QSize>().height();
|
||||||
|
// get text width, see QItemDelegatePrivate::displayRect
|
||||||
|
const QString text = itemText(index).replace('\t', m_tabString);
|
||||||
|
const QRect textMaxRect(0, 0, INT_MAX / 256, height);
|
||||||
|
const QRect textLayoutRect = textRectangle(nullptr, textMaxRect, info.option.font, text);
|
||||||
|
const QRect textRect(info.textRect.x(), info.textRect.y(), textLayoutRect.width(), height);
|
||||||
|
const QRect layoutRect = info.checkRect | info.pixmapRect | info.lineNumberRect | textRect;
|
||||||
|
return QSize(layoutRect.x(), layoutRect.y()) + layoutRect.size();
|
||||||
|
}
|
||||||
|
|
||||||
// returns the width of the line number area
|
// returns the width of the line number area
|
||||||
int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option,
|
int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
const QRect &rect,
|
const QRect &rect,
|
||||||
const QModelIndex &index) const
|
const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
static const int lineNumberAreaHorizontalPadding = 4;
|
|
||||||
int lineNumber = index.model()->data(index, ItemDataRoles::ResultBeginLineNumberRole).toInt();
|
|
||||||
if (lineNumber < 1)
|
|
||||||
return 0;
|
|
||||||
const bool isSelected = option.state & QStyle::State_Selected;
|
const bool isSelected = option.state & QStyle::State_Selected;
|
||||||
QString lineText = QString::number(lineNumber);
|
const std::pair<int, QString> numberInfo = lineNumberInfo(option, index);
|
||||||
int minimumLineNumberDigits = qMax((int)m_minimumLineNumberDigits, lineText.count());
|
if (numberInfo.first == 0)
|
||||||
int fontWidth = painter->fontMetrics().horizontalAdvance(QString(minimumLineNumberDigits, QLatin1Char('0')));
|
return 0;
|
||||||
int lineNumberAreaWidth = lineNumberAreaHorizontalPadding + fontWidth + lineNumberAreaHorizontalPadding;
|
|
||||||
QRect lineNumberAreaRect(rect);
|
QRect lineNumberAreaRect(rect);
|
||||||
lineNumberAreaRect.setWidth(lineNumberAreaWidth);
|
lineNumberAreaRect.setWidth(numberInfo.first);
|
||||||
|
|
||||||
QPalette::ColorGroup cg = QPalette::Normal;
|
QPalette::ColorGroup cg = QPalette::Normal;
|
||||||
if (!(option.state & QStyle::State_Active))
|
if (!(option.state & QStyle::State_Active))
|
||||||
@@ -137,9 +188,9 @@ int SearchResultTreeItemDelegate::drawLineNumber(QPainter *painter, const QStyle
|
|||||||
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, nullptr) + 1;
|
const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, nullptr) + 1;
|
||||||
|
|
||||||
const QRect rowRect = lineNumberAreaRect.adjusted(-textMargin, 0, textMargin-lineNumberAreaHorizontalPadding, 0);
|
const QRect rowRect = lineNumberAreaRect.adjusted(-textMargin, 0, textMargin-lineNumberAreaHorizontalPadding, 0);
|
||||||
QItemDelegate::drawDisplay(painter, opt, rowRect, lineText);
|
QItemDelegate::drawDisplay(painter, opt, rowRect, numberInfo.second);
|
||||||
|
|
||||||
return lineNumberAreaWidth;
|
return numberInfo.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchResultTreeItemDelegate::drawText(QPainter *painter,
|
void SearchResultTreeItemDelegate::drawText(QPainter *painter,
|
||||||
@@ -147,18 +198,15 @@ void SearchResultTreeItemDelegate::drawText(QPainter *painter,
|
|||||||
const QRect &rect,
|
const QRect &rect,
|
||||||
const QModelIndex &index) const
|
const QModelIndex &index) const
|
||||||
{
|
{
|
||||||
QString text = index.model()->data(index, Qt::DisplayRole).toString();
|
const QString text = itemText(index);
|
||||||
// show number of subresults in displayString
|
|
||||||
if (index.model()->hasChildren(index)) {
|
|
||||||
text += QLatin1String(" (")
|
|
||||||
+ QString::number(index.model()->rowCount(index))
|
|
||||||
+ QLatin1Char(')');
|
|
||||||
}
|
|
||||||
|
|
||||||
const int searchTermStart = index.model()->data(index, ItemDataRoles::ResultBeginColumnNumberRole).toInt();
|
const int searchTermStart = index.model()->data(index, ItemDataRoles::ResultBeginColumnNumberRole).toInt();
|
||||||
int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt();
|
int searchTermLength = index.model()->data(index, ItemDataRoles::SearchTermLengthRole).toInt();
|
||||||
if (searchTermStart < 0 || searchTermStart >= text.length() || searchTermLength < 1) {
|
if (searchTermStart < 0 || searchTermStart >= text.length() || searchTermLength < 1) {
|
||||||
QItemDelegate::drawDisplay(painter, option, rect, text.replace(QLatin1Char('\t'), m_tabString));
|
QItemDelegate::drawDisplay(painter,
|
||||||
|
option,
|
||||||
|
rect,
|
||||||
|
QString(text).replace(QLatin1Char('\t'), m_tabString));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -30,20 +30,31 @@
|
|||||||
namespace Core {
|
namespace Core {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
struct LayoutInfo
|
||||||
|
{
|
||||||
|
QRect checkRect;
|
||||||
|
QRect pixmapRect;
|
||||||
|
QRect textRect;
|
||||||
|
QRect lineNumberRect;
|
||||||
|
QIcon icon;
|
||||||
|
Qt::CheckState checkState;
|
||||||
|
QStyleOptionViewItem option;
|
||||||
|
};
|
||||||
|
|
||||||
class SearchResultTreeItemDelegate: public QItemDelegate
|
class SearchResultTreeItemDelegate: public QItemDelegate
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SearchResultTreeItemDelegate(int tabWidth, QObject *parent = nullptr);
|
SearchResultTreeItemDelegate(int tabWidth, QObject *parent = nullptr);
|
||||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||||
void setTabWidth(int width);
|
void setTabWidth(int width);
|
||||||
|
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||||
private:
|
private:
|
||||||
|
LayoutInfo getLayoutInfo(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||||
int drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QModelIndex &index) const;
|
int drawLineNumber(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QModelIndex &index) const;
|
||||||
void drawText(QPainter *painter, const QStyleOptionViewItem &option,
|
void drawText(QPainter *painter, const QStyleOptionViewItem &option,
|
||||||
const QRect &rect, const QModelIndex &index) const;
|
const QRect &rect, const QModelIndex &index) const;
|
||||||
|
|
||||||
QString m_tabString;
|
QString m_tabString;
|
||||||
static const int m_minimumLineNumberDigits = 6;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
@@ -44,6 +44,8 @@ SearchResultTreeView::SearchResultTreeView(QWidget *parent)
|
|||||||
setIndentation(14);
|
setIndentation(14);
|
||||||
setUniformRowHeights(true);
|
setUniformRowHeights(true);
|
||||||
setExpandsOnDoubleClick(true);
|
setExpandsOnDoubleClick(true);
|
||||||
|
header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
header()->setStretchLastSection(false);
|
||||||
header()->hide();
|
header()->hide();
|
||||||
|
|
||||||
connect(this, &SearchResultTreeView::activated,
|
connect(this, &SearchResultTreeView::activated,
|
||||||
@@ -93,6 +95,13 @@ void SearchResultTreeView::keyPressEvent(QKeyEvent *event)
|
|||||||
TreeView::keyPressEvent(event);
|
TreeView::keyPressEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool SearchResultTreeView::event(QEvent *e)
|
||||||
|
{
|
||||||
|
if (e->type() == QEvent::Resize)
|
||||||
|
header()->setMinimumSectionSize(width());
|
||||||
|
return TreeView::event(e);
|
||||||
|
}
|
||||||
|
|
||||||
void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index)
|
void SearchResultTreeView::emitJumpToSearchResult(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
if (model()->data(index, ItemDataRoles::IsGeneratedRole).toBool())
|
if (model()->data(index, ItemDataRoles::IsGeneratedRole).toBool())
|
||||||
|
@@ -50,6 +50,7 @@ public:
|
|||||||
void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
void addResults(const QList<SearchResultItem> &items, SearchResult::AddMode mode);
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *event) override;
|
void keyPressEvent(QKeyEvent *event) override;
|
||||||
|
bool event(QEvent *e) override;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void jumpToSearchResult(const SearchResultItem &item);
|
void jumpToSearchResult(const SearchResultItem &item);
|
||||||
|
Reference in New Issue
Block a user