函数,将焦点移动到下一位:
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()
信号。
这个函数通常用于编程方式控制滑动条的值,例如响应用户操作或者根据程序逻辑动态更新滑动条的值。