搜索文本
最初的做法
通过递归调用QTextDocument::find接口,获取对应的QTextCursor,将对应QTextCursor改变文字颜色。
遇到的问题
使用QTextDocument::find的接口去搜索高亮,效率很低,并且无法放在子线程中进行搜索。在渲染的时候,性能也很差,用户体验很差。在代码逻辑上,要不断控制文字颜色的改变,导致代码层面比较混乱,容易出现bug。
解决方案
参考了qtcreator和kate的方式,同时也参考了一下vscode(虽然是用js实现的)。qtcreator和kate方式,都是通过QRegularExpression进行匹配,在界面渲染的时候(paintEvent)中,通过firstVisibleBlock接口,获取当前可视界面中的第一个block,然后逐个block进行搜索,直到搜索过的block总高度超过可视高度才结束。每遍历一个可视的block,则获取对应这个block的text,将text进行正则表达式匹配,获取到对应的position,根据开始位置和结束位置,获取到对应的一块矩形区域,然后将矩形区域填充颜色。
关键代码
void TextEditorWidgetPrivate::paintSearchResultOverlay(const PaintEventData &data,
QPainter &painter) const
{
m_searchResultOverlay->clear();
if (m_searchExpr.pattern().isEmpty() || !m_searchExpr.isValid())
return;
const int margin = 5;
QTextBlock block = data.block;
QPointF offset = data.offset;
// 计算可视范围
while (block.isValid()) {
QRectF blockBoundingRect = q->blockBoundingRect(block).translated(offset);
if (blockBoundingRect.bottom() >= data.eventRect.top() - margin
&& blockBoundingRect.top() <= data.eventRect.bottom() + margin) {
// 高亮对应的数据
highlightSearchResults(block, data);
}
offset.ry() += blockBoundingRect.height();
if (offset.y() > data.viewportRect.height() + margin)
break;
block = TextEditor::nextVisibleBlock(block, data.doc);
}
m_searchResultOverlay->fill(&painter,
data.searchResultFormat.background().color(),
data.eventRect);
}
// 具体高亮逻辑在TextEditorOverlay中,代码太多就不一一展示