Qt的一些经验总结(5)-- 搜索文本

126 阅读1分钟

搜索文本

最初的做法

通过递归调用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中,代码太多就不一一展示