QT源码解析之富文本文档函数QTextBrowser::focusNextPrevChild

函数,将焦点移动到下一位:

virtual bool focusNextPrevChild(bool next) override;

bool QTextBrowser::focusNextPrevChild(bool next)
{
    Q_D(QTextBrowser);
    if (d->control->setFocusToNextOrPreviousAnchor(next)) {
#ifdef QT_KEYPAD_NAVIGATION
        // Might need to synthesize a highlight event.
        if (d->prevFocus != d->control->textCursor() && d->control->textCursor().hasSelection()) {
            const QString href = d->control->anchorAtCursor();
            QUrl url = d->resolveUrl(href);
            emitHighlighted(url);
        }
        d->prevFocus = d->control->textCursor();
#endif
        return true;
    } else {
#ifdef QT_KEYPAD_NAVIGATION
        // We assume we have no highlight now.
        emitHighlighted(QUrl());
#endif
    }
    return QTextEdit::focusNextPrevChild(next);
}

1.调用d模式函数

d->control->setFocusToNextOrPreviousAnchor(next)

bool QWidgetTextControl::setFocusToNextOrPreviousAnchor(bool next)
{
    Q_D(QWidgetTextControl);
    if (!(d->interactionFlags & Qt::LinksAccessibleByKeyboard))
        return false;
    QRectF crect = selectionRect();
    emit updateRequest(crect);
    // If we don't have a current anchor, we start from the start/end
    if (!d->cursor.hasSelection()) {
        d->cursor = QTextCursor(d->doc);
        if (next)
            d->cursor.movePosition(QTextCursor::Start);
        else
            d->cursor.movePosition(QTextCursor::End);
    }
    QTextCursor newAnchor;
    if (findNextPrevAnchor(d->cursor, next, newAnchor)) {
        d->cursor = newAnchor;
        d->cursorIsFocusIndicator = true;
    } else {
        d->cursor.clearSelection();
    }
    if (d->cursor.hasSelection()) {
        crect = selectionRect();
        emit updateRequest(crect);
        emit visibilityRequest(crect);
        return true;
    } else {
        return false;
    }
}

2.找到下一个焦点:

findNextPrevAnchor(d->cursor, next, newAnchor)

这个函数中,前面是消除上一个状态,末尾的函数:

newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);

才是设置下个焦点的状态:


bool QWidgetTextControl::findNextPrevAnchor(const QTextCursor &startCursor, bool next, QTextCursor &newAnchor)
{
    Q_D(QWidgetTextControl);

    int anchorStart = -1;
    QString anchorHref;
    int anchorEnd = -1;

    if (next) {
        const int startPos = startCursor.selectionEnd();

        QTextBlock block = d->doc->findBlock(startPos);
        QTextBlock::Iterator it = block.begin();

        while (!it.atEnd() && it.fragment().position() < startPos)
            ++it;

        while (block.isValid()) {
            anchorStart = -1;

            // find next anchor
            for (; !it.atEnd(); ++it) {
                const QTextFragment fragment = it.fragment();
                const QTextCharFormat fmt = fragment.charFormat();

                if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
                    anchorStart = fragment.position();
                    anchorHref = fmt.anchorHref();
                    break;
                }
            }

            if (anchorStart != -1) {
                anchorEnd = -1;

                // find next non-anchor fragment
                for (; !it.atEnd(); ++it) {
                    const QTextFragment fragment = it.fragment();
                    const QTextCharFormat fmt = fragment.charFormat();

                    if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
                        anchorEnd = fragment.position();
                        break;
                    }
                }

                if (anchorEnd == -1)
                    anchorEnd = block.position() + block.length() - 1;

                // make found selection
                break;
            }

            block = block.next();
            it = block.begin();
        }
    } else {
        int startPos = startCursor.selectionStart();
        if (startPos > 0)
            --startPos;

        QTextBlock block = d->doc->findBlock(startPos);
        QTextBlock::Iterator blockStart = block.begin();
        QTextBlock::Iterator it = block.end();

        if (startPos == block.position()) {
            it = block.begin();
        } else {
            do {
                if (it == blockStart) {
                    it = QTextBlock::Iterator();
                    block = QTextBlock();
                } else {
                    --it;
                }
            } while (!it.atEnd() && it.fragment().position() + it.fragment().length() - 1 > startPos);
        }

        while (block.isValid()) {
            anchorStart = -1;

            if (!it.atEnd()) {
                do {
                    const QTextFragment fragment = it.fragment();
                    const QTextCharFormat fmt = fragment.charFormat();

                    if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) {
                        anchorStart = fragment.position() + fragment.length();
                        anchorHref = fmt.anchorHref();
                        break;
                    }

                    if (it == blockStart)
                        it = QTextBlock::Iterator();
                    else
                        --it;
                } while (!it.atEnd());
            }

            if (anchorStart != -1 && !it.atEnd()) {
                anchorEnd = -1;

                do {
                    const QTextFragment fragment = it.fragment();
                    const QTextCharFormat fmt = fragment.charFormat();

                    if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) {
                        anchorEnd = fragment.position() + fragment.length();
                        break;
                    }

                    if (it == blockStart)
                        it = QTextBlock::Iterator();
                    else
                        --it;
                } while (!it.atEnd());

                if (anchorEnd == -1)
                    anchorEnd = qMax(0, block.position());

                break;
            }

            block = block.previous();
            it = block.end();
            if (it != block.begin())
                --it;
            blockStart = block.begin();
        }

    }

    if (anchorStart != -1 && anchorEnd != -1) {
        newAnchor = d->cursor;
        newAnchor.setPosition(anchorStart);
        newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);
        return true;
    }

    return false;
}

一些使用的类的说明:

QTextBlock 类是 Qt 中用于处理富文本编辑的一部分。它代表了一个文本块(block)或者段落,可以包含一行或多行文本。在 Qt 的文本编辑框架中,文本被组织成一个个的文本块。QTextBlock 提供了访问文本块内容以及与文本格式相关的信息的方法。你可以使用它来获取文本块的文本内容、格式信息、以及它在文档中的位置等。

QTextFragment 类表示文本块中具有相同格式的一部分文本。在 Qt 的富文本编辑框架中,文本块(QTextBlock)可以包含一个或多个文本片段。每个文本片段都有一个相同的格式,例如字体、颜色、样式等。QTextFragment 提供了访问文本片段内容和格式的方法,以及获取它在文本中的位置等信息。它通常与 QTextCursor 一起使用,用于在文本中进行定位、遍历和操作。

QTextBlock::isValid() 是一个成员函数,用于检查 QTextBlock 对象是否有效。在 Qt 中,QTextBlock 代表一个文本块(或段落),它可以包含一行或多行文本。当你使用文本编辑框架时,你可能会创建 QTextBlock 对象来代表文本的不同部分。

如果 QTextBlock 对象是有效的,即它指向了文档中的一个实际的文本块,则 isValid() 返回 true。如果 QTextBlock 对象是无效的,即它未指向任何文本块,则 isValid() 返回 false

4.设置下一个状态

newAnchor.setPosition(anchorEnd, QTextCursor::KeepAnchor);

先描述一个枚举值的作用(QTextCursor::KeepAnchor):

在 Qt 中,QTextCursor::KeepAnchor 是一个枚举值,用于指定在移动文本光标时是否保持选取状态。具体来说,当你在使用 QTextCursor 对象选择文本时,可以通过设置选取模式为 QTextCursor::KeepAnchor 来保持之前的选取状态。

举个例子,假设你有一个 QTextCursor 对象 cursor,你可以这样使用它来选择文本:

cursor.movePosition(QTextCursor::Start);
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);

在这个例子中,movePosition() 函数将文本光标移动到文本的起始位置,然后将文本光标移动到文本的末尾,并保持之前的选取状态(即保持选中整个文本)

回到正题:setPosition()函数:

void QTextCursor::setPosition(int pos, MoveMode m)
{
    if (!d || !d->priv)
        return;

    if (pos < 0 || pos >= d->priv->length()) {
        qWarning("QTextCursor::setPosition: Position '%d' out of range", pos);
        return;
    }

    d->setPosition(pos);
    if (m == MoveAnchor) {
        d->anchor = pos;
        d->adjusted_anchor = pos;
    } else { // keep anchor
        QTextCursor::MoveOperation op;
        if (pos < d->anchor)
            op = QTextCursor::Left;
        else
            op = QTextCursor::Right;
        d->adjustCursor(op);
    }
    d->setX();
}

末尾,setX();

5.

void QTextCursorPrivate::setX()

void QTextCursorPrivate::setX()
{
    if (priv->isInEditBlock() || priv->inContentsChange) {
        x = -1; // mark dirty
        return;
    }

    QTextBlock block = this->block();
    const QTextLayout *layout = blockLayout(block);
    int pos = position - block.position();

    QTextLine line = layout->lineForTextPosition(pos);
    if (line.isValid())
        x = line.cursorToX(pos);
    else
        x = -1; // delayed init.  Makes movePosition() call setX later on again.
}

重要函数:

QTextLine::cursorToX()QTextLine 类的一个成员函数,用于将文本行中的字符索引(光标位置)映射到相对于文本行起始位置的 x 坐标。

具体来说,它接受一个整数参数,表示字符的索引位置,然后返回该字符在文本行中的 x 坐标位置。这个坐标是相对于文本行的左侧(或顶部,取决于文本方向)的偏移量。

这个函数通常在绘制文本时使用,以确定每个字符的位置,从而正确地渲染文本

到这里,我们解析了如何找到指定的上一个(下一个)焦点的位置,那么,如何设置焦点,回到开头函数:

findNextPrevAnchor(d->cursor, next, newAnchor);

下一步是:

    if (d->cursor.hasSelection()) {
        crect = selectionRect();
        emit updateRequest(crect);
        emit visibilityRequest(crect);
        return true;
    } else {
        return false;
    }

两个信号:

QWidgetTextControl::updateRequest(const QRectF &rect = QRectF()) 是一个虚函数,用于在文本控件需要更新时发送更新请求。

这个函数通常由文本控件内部的布局管理器或渲染器调用,以通知文本控件需要重新绘制指定区域的内容。参数 rect 指定了需要更新的区域,如果不提供参数,默认值为一个空的矩形,表示需要更新整个文本控件的内容。

具体来说,当文本内容发生变化、文本格式发生改变、窗口大小改变等需要重新绘制文本控件内容的情况下,会调用这个函数发送更新请求。

在你的子类中重写这个函数,可以实现自定义的更新策略,例如在文本控件的内容变化时重新计算布局、重新绘制指定区域的文本内容等。

QWidgetTextControl::visibilityRequest(const QRectF &rect) 是一个虚函数,用于在文本控件需要调整可见区域时发送可见性请求。

这个函数通常由文本控件内部的布局管理器或渲染器调用,以通知文本控件需要调整可见区域,以确保用户能够看到文本内容的正确部分。

参数 rect 指定了需要调整为可见区域的矩形区域。调用此函数时,文本控件会尝试调整显示区域以确保指定的矩形区域可见。

你可以在你的子类中重写这个函数,以实现自定义的可见性调整策略,例如在文本控件滚动时根据滚动位置动态调整可见区域。

追踪信号:QWidgetTextControl::visibilityRequest(const QRectF &rect):

QTextEditPrivate::init(const QString &html)中:

QObject::connect(control, SIGNAL(visibilityRequest(QRectF)), q, SLOT(_q_ensureVisible(QRectF)));

槽函数:


// rect is in content coordinates
void QTextEditPrivate::_q_ensureVisible(const QRectF &_rect)
{
    const QRect rect = _rect.toRect();
    if ((vbar->isVisible() && vbar->maximum() < rect.bottom())
        || (hbar->isVisible() && hbar->maximum() < rect.right()))
        _q_adjustScrollbars();
    const int visibleWidth = viewport->width();
    const int visibleHeight = viewport->height();
    const bool rtl = q_func()->isRightToLeft();
    if (rect.x() < horizontalOffset()) {
        if (rtl)
            hbar->setValue(hbar->maximum() - rect.x());
        else
            hbar->setValue(rect.x());
    } else if (rect.x() + rect.width() > horizontalOffset() + visibleWidth) {
        if (rtl)
            hbar->setValue(hbar->maximum() - (rect.x() + rect.width() - visibleWidth));
        else
            hbar->setValue(rect.x() + rect.width() - visibleWidth);
    }
    if (rect.y() < verticalOffset())
        vbar->setValue(rect.y());
    else if (rect.y() + rect.height() > verticalOffset() + visibleHeight)
        vbar->setValue(rect.y() + rect.height() - visibleHeight);
}

一个重要函数:

    inline int horizontalOffset() const
    { return q_func()->isRightToLeft() ? (hbar->maximum() - hbar->value()) : hbar->value(); }

QTextEditPrivate::horizontalOffset() const 是 QTextEdit 类的私有成员函数,用于获取 QTextEdit 控件的水平偏移量。

这个函数通常在 QTextEdit 内部的布局管理器或渲染器中使用,用于确定文本内容在水平方向上的偏移量。这个偏移量指示了文本内容相对于 QTextEdit 控件左侧边缘的距离。

由于这是 QTextEdit 的私有函数,它主要在 QTextEdit 的内部实现中使用,而不是在公共接口中直接调用。

另外一个重要函数:setValue:

void QAbstractSlider::setValue(int value)
{
    Q_D(QAbstractSlider);
    value = d->bound(value);
    if (d->value == value && d->position == value)
        return;
    d->value = value;
    if (d->position != value) {
        d->position = value;
        if (d->pressed)
            emit sliderMoved((d->position = value));
    }
#ifndef QT_NO_ACCESSIBILITY
    QAccessibleValueChangeEvent event(this, d->value);
    QAccessible::updateAccessibility(&event);
#endif
    sliderChange(SliderValueChange);
    emit valueChanged(value);
}

QAbstractSlider::setValue(int value) 是 Qt 中 QAbstractSlider 类的成员函数之一,用于设置滑动条(Slider)的当前值。

这个函数接受一个整数参数 value,表示要设置的新的滑动条值。当调用 setValue() 函数时,滑动条将更新到指定的值,并且会触发相应的信号,如 valueChanged() 信号。

这个函数通常用于编程方式控制滑动条的值,例如响应用户操作或者根据程序逻辑动态更新滑动条的值。

至此,追踪断掉了,明天继续吧

  • 24
    点赞
  • 51
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值