Commit 29b073e9 authored by mae's avatar mae
Browse files

Refactor block selection

Block selection was "broken" when using tabs, or rather
incomplete: It treated tabs as normal characters, which
has shown to be unexpected by people using tabs in code.

The new implementation has a vastly improved find scope
as well. In addition, creating a blog selection with
mouse or keyboard feels a lot more solid now, as the
actual selection is detached from possible valid cursor
positions.

Task-number: QTCREATORBUG-1541
parent 9b338fbb
......@@ -39,14 +39,16 @@ using namespace Find;
BaseTextFind::BaseTextFind(QTextEdit *editor)
: m_editor(editor)
, m_findScopeVerticalBlockSelection(0)
, m_findScopeVerticalBlockSelectionFirstColumn(-1)
, m_findScopeVerticalBlockSelectionLastColumn(-1)
, m_incrementalStartPos(-1)
{
}
BaseTextFind::BaseTextFind(QPlainTextEdit *editor)
: m_plaineditor(editor)
, m_findScopeVerticalBlockSelection(0)
, m_findScopeVerticalBlockSelectionFirstColumn(-1)
, m_findScopeVerticalBlockSelectionLastColumn(-1)
, m_incrementalStartPos(-1)
{
}
......@@ -268,17 +270,16 @@ QTextCursor BaseTextFind::findOne(const QRegExp &expr, const QTextCursor &from,
if (candidate.isNull())
return candidate;
if (!m_findScopeVerticalBlockSelection)
if (m_findScopeVerticalBlockSelectionFirstColumn < 0)
return candidate;
forever {
if (!inScope(candidate.selectionStart(), candidate.selectionEnd()))
return candidate;
QTextCursor b = candidate;
b.setPosition(candidate.selectionStart());
QTextCursor e = candidate;
e.setPosition(candidate.selectionEnd());
if (b.positionInBlock() >= m_findScopeStart.positionInBlock() + 1
&& e.positionInBlock() <= m_findScopeStart.positionInBlock() + 1 + m_findScopeVerticalBlockSelection)
bool inVerticalFindScope = false;
QMetaObject::invokeMethod(m_plaineditor, "inFindScope", Qt::DirectConnection,
Q_RETURN_ARG(bool, inVerticalFindScope),
Q_ARG(QTextCursor, candidate));
if (inVerticalFindScope)
return candidate;
candidate = document()->find(expr, candidate, options);
}
......@@ -299,23 +300,19 @@ void BaseTextFind::defineFindScope()
if (cursor.hasSelection() && cursor.block() != cursor.document()->findBlock(cursor.anchor())) {
m_findScopeStart = QTextCursor(document()->docHandle(), qMax(0, cursor.selectionStart()-1));
m_findScopeEnd = QTextCursor(document()->docHandle(), cursor.selectionEnd());
m_findScopeVerticalBlockSelection = 0;
int verticalBlockSelection = 0;
if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelection") >= 0)
verticalBlockSelection = m_plaineditor->property("verticalBlockSelection").toInt();
if (verticalBlockSelection) {
QTextCursor findScopeVisualStart(document()->docHandle(), cursor.selectionStart());
int findScopeFromColumn = qMin(findScopeVisualStart.positionInBlock(),
m_findScopeEnd.positionInBlock());
int findScopeToColumn = findScopeFromColumn + verticalBlockSelection;
m_findScopeStart.setPosition(findScopeVisualStart.block().position() + findScopeFromColumn - 1);
m_findScopeEnd.setPosition(m_findScopeEnd.block().position()
+ qMin(m_findScopeEnd.block().length()-1, findScopeToColumn));
m_findScopeVerticalBlockSelection = verticalBlockSelection;
m_findScopeVerticalBlockSelectionFirstColumn = -1;
m_findScopeVerticalBlockSelectionLastColumn = -1;
if (m_plaineditor && m_plaineditor->metaObject()->indexOfProperty("verticalBlockSelectionFirstColumn") >= 0) {
m_findScopeVerticalBlockSelectionFirstColumn
= m_plaineditor->property("verticalBlockSelectionFirstColumn").toInt();
m_findScopeVerticalBlockSelectionLastColumn
= m_plaineditor->property("verticalBlockSelectionLastColumn").toInt();
}
emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
emit findScopeChanged(m_findScopeStart, m_findScopeEnd,
m_findScopeVerticalBlockSelectionFirstColumn,
m_findScopeVerticalBlockSelectionLastColumn);
cursor.setPosition(m_findScopeStart.position()+1);
setTextCursor(cursor);
} else {
......@@ -327,6 +324,9 @@ void BaseTextFind::clearFindScope()
{
m_findScopeStart = QTextCursor();
m_findScopeEnd = QTextCursor();
m_findScopeVerticalBlockSelection = 0;
emit findScopeChanged(m_findScopeStart, m_findScopeEnd, m_findScopeVerticalBlockSelection);
m_findScopeVerticalBlockSelectionFirstColumn = -1;
m_findScopeVerticalBlockSelectionLastColumn = -1;
emit findScopeChanged(m_findScopeStart, m_findScopeEnd,
m_findScopeVerticalBlockSelectionFirstColumn,
m_findScopeVerticalBlockSelectionLastColumn);
}
......@@ -72,7 +72,9 @@ public:
signals:
void highlightAll(const QString &txt, Find::FindFlags findFlags);
void findScopeChanged(const QTextCursor &start, const QTextCursor &end, int verticalBlockSelection);
void findScopeChanged(const QTextCursor &start, const QTextCursor &end,
int verticalBlockSelectionFirstColumn,
int verticalBlockSelectionLastColumn);
private:
bool find(const QString &txt,
......@@ -89,7 +91,8 @@ private:
QPointer<QPlainTextEdit> m_plaineditor;
QTextCursor m_findScopeStart;
QTextCursor m_findScopeEnd;
int m_findScopeVerticalBlockSelection;
int m_findScopeVerticalBlockSelectionFirstColumn;
int m_findScopeVerticalBlockSelectionLastColumn;
bool inScope(int startPosition, int endPosition) const;
QTextCursor findOne(const QRegExp &expr, const QTextCursor &from, QTextDocument::FindFlags options) const;
int m_incrementalStartPos;
......
......@@ -591,7 +591,11 @@ void OutputWindow::appendText(const QString &textIn, const QTextCharFormat &form
bool OutputWindow::isScrollbarAtBottom() const
{
return verticalScrollBar()->value() == verticalScrollBar()->maximum();
return isVisible()
&& (blockBoundingRect(document()->lastBlock()).bottom()
+ contentOffset().y() <= viewport()->rect().bottom());
// return verticalScrollBar()->value() == verticalScrollBar()->maximum();
}
void OutputWindow::scrollToBottom()
......
......@@ -710,12 +710,12 @@ void BaseTextEditor::editorContentsChange(int position, int charsRemoved, int ch
void BaseTextEditor::slotSelectionChanged()
{
bool changed = (d->m_inBlockSelectionMode != d->m_lastEventWasBlockSelectionEvent);
d->m_inBlockSelectionMode = d->m_lastEventWasBlockSelectionEvent;
if (changed || d->m_inBlockSelectionMode)
if (d->m_inBlockSelectionMode && !textCursor().hasSelection()) {
d->m_inBlockSelectionMode = false;
d->m_blockSelection.clear();
viewport()->update();
if (!d->m_inBlockSelectionMode)
d->m_blockSelectionExtraX = 0;
}
if (!d->m_selectBlockAnchor.isNull() && !textCursor().hasSelection())
d->m_selectBlockAnchor = QTextCursor();
......@@ -1092,9 +1092,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
d->m_moveLineUndoHack = false;
d->clearVisibleFoldedBlock();
QKeyEvent *original_e = e;
d->m_lastEventWasBlockSelectionEvent = false;
if (e->key() == Qt::Key_Escape) {
if (d->m_snippetOverlay->isVisible()) {
e->accept();
......@@ -1182,16 +1179,20 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
} else if (!ro
&& (e == QKeySequence::MoveToStartOfBlock
|| e == QKeySequence::SelectStartOfBlock)){
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
d->m_lastEventWasBlockSelectionEvent = true;
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
e->accept();
return;
}
handleHomeKey(e == QKeySequence::SelectStartOfBlock);
e->accept();
return;
} else if (!ro
&& (e == QKeySequence::MoveToStartOfLine
|| e == QKeySequence::SelectStartOfLine)){
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier))
d->m_lastEventWasBlockSelectionEvent = true;
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
e->accept();
return;
}
QTextCursor cursor = textCursor();
if (QTextLayout *layout = cursor.block().layout()) {
if (layout->lineForTextPosition(cursor.position() - cursor.block().position()).lineNumber() == 0) {
......@@ -1273,36 +1274,23 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
return;
}
// fall through
case Qt::Key_End:
case Qt::Key_Right:
case Qt::Key_Left:
#ifndef Q_WS_MAC
if ((e->modifiers() & (Qt::AltModifier | Qt::ShiftModifier)) == (Qt::AltModifier | Qt::ShiftModifier)) {
d->m_lastEventWasBlockSelectionEvent = true;
if (d->m_inBlockSelectionMode) {
if (e->key() == Qt::Key_Right && textCursor().atBlockEnd()) {
d->m_blockSelectionExtraX++;
viewport()->update();
e->accept();
return;
} else if (e->key() == Qt::Key_Left && d->m_blockSelectionExtraX > 0) {
d->m_blockSelectionExtraX--;
e->accept();
viewport()->update();
return;
}
}
e = new QKeyEvent(
e->type(),
e->key(),
e->modifiers() & ~Qt::AltModifier,
e->text(),
e->isAutoRepeat(),
e->count()
);
int diff_row = 0;
int diff_col = 0;
if (e->key() == Qt::Key_Up)
diff_row = -1;
else if (e->key() == Qt::Key_Down)
diff_row = 1;
else if (e->key() == Qt::Key_Left)
diff_col = -1;
else if (e->key() == Qt::Key_Right)
diff_col = 1;
handleBlockSelection(diff_row, diff_col);
e->accept();
return;
}
#endif
break;
......@@ -1395,8 +1383,6 @@ void BaseTextEditor::keyPressEvent(QKeyEvent *e)
maybeRequestAutoCompletion(e->text().at(0));
}
if (e != original_e)
delete e;
}
void BaseTextEditor::maybeRequestAutoCompletion(const QChar &ch)
......@@ -1934,10 +1920,9 @@ BaseTextEditorPrivate::BaseTextEditorPrivate()
m_editable(0),
m_actionHack(0),
m_inBlockSelectionMode(false),
m_lastEventWasBlockSelectionEvent(false),
m_blockSelectionExtraX(-1),
m_moveLineUndoHack(false),
m_findScopeVerticalBlockSelection(0),
m_findScopeVerticalBlockSelectionFirstColumn(-1),
m_findScopeVerticalBlockSelectionLastColumn(-1),
m_highlightBlocksTimer(0),
m_requestAutoCompletionRevision(0),
m_requestAutoCompletionPosition(0),
......@@ -2158,12 +2143,6 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
int idx = -1;
int l = 1;
int m_findScopeFirstColumn = 0;
if (!m_findScopeStart.isNull() && m_findScopeVerticalBlockSelection) {
m_findScopeFirstColumn = m_findScopeStart.positionInBlock() + 1;
}
while (idx < text.length()) {
idx = m_searchExpr.indexIn(text, idx + l);
if (idx < 0)
......@@ -2176,17 +2155,8 @@ void BaseTextEditorPrivate::highlightSearchResults(const QTextBlock &block,
|| (idx + l < text.length() && text.at(idx + l).isLetterOrNumber())))
continue;
if (!m_findScopeStart.isNull()) {
if (blockPosition + idx < m_findScopeStart.position()
|| blockPosition + idx + l > m_findScopeEnd.position())
continue;
if (m_findScopeVerticalBlockSelection) {
if (idx < m_findScopeFirstColumn
|| idx + l > m_findScopeFirstColumn + m_findScopeVerticalBlockSelection)
continue;
}
}
if (!q->inFindScope(blockPosition + idx, blockPosition + idx + l))
continue;
overlay->addOverlaySelection(blockPosition + idx,
blockPosition + idx + l,
......@@ -2216,6 +2186,7 @@ void BaseTextEditorPrivate::clearBlockSelection()
{
if (m_inBlockSelectionMode) {
m_inBlockSelectionMode = false;
m_blockSelection.clear();
QTextCursor cursor = q->textCursor();
cursor.clearSelection();
q->setTextCursor(cursor);
......@@ -2224,68 +2195,77 @@ void BaseTextEditorPrivate::clearBlockSelection()
QString BaseTextEditorPrivate::copyBlockSelection()
{
QString text;
QString selection;
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
return text;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
QTextBlock block = startBlock;
if (!m_inBlockSelectionMode)
return selection;
const TabSettings &ts = q->tabSettings();
QTextBlock block = m_blockSelection.firstBlock.block();
QTextBlock lastBlock = m_blockSelection.lastBlock.block();
for (;;) {
QString text = block.text();
int startOffset = 0;
int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
int endOffset = 0;
int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
text += cursor.selectedText();
if (block == endBlock)
if (startPos == endPos) {
selection += QString(endOffset - startOffset, QLatin1Char(' '));
} else {
if (startOffset < 0)
selection += QString(-startOffset, QLatin1Char(' '));
if (endOffset < 0)
--endPos;
selection += text.mid(startPos, endPos - startPos);
if (endOffset < 0) {
selection += QString(ts.m_tabSize + endOffset, QLatin1Char(' '));
} else if (endOffset > 0) {
selection += QString(endOffset, QLatin1Char(' '));
}
}
if (block == lastBlock)
break;
text += QLatin1Char('\n');
selection += QLatin1Char('\n');
block = block.next();
}
return text;
return selection;
}
void BaseTextEditorPrivate::removeBlockSelection(const QString &text)
{
QTextCursor cursor = q->textCursor();
if (!cursor.hasSelection())
if (!cursor.hasSelection() || !m_inBlockSelectionMode)
return;
QTextDocument *doc = q->document();
int start = cursor.selectionStart();
int end = cursor.selectionEnd();
QTextBlock startBlock = doc->findBlock(start);
int columnA = start - startBlock.position();
QTextBlock endBlock = doc->findBlock(end);
int columnB = end - endBlock.position();
int firstColumn = qMin(columnA, columnB);
int lastColumn = qMax(columnA, columnB) + m_blockSelectionExtraX;
int cursorPosition = cursor.selectionStart();
cursor.clearSelection();
cursor.beginEditBlock();
QTextBlock block = startBlock;
const TabSettings &ts = q->tabSettings();
QTextBlock block = m_blockSelection.firstBlock.block();
QTextBlock lastBlock = m_blockSelection.lastBlock.block();
for (;;) {
QString text = block.text();
int startOffset = 0;
int startPos = ts.positionAtColumn(text, m_blockSelection.firstVisualColumn, &startOffset);
int endOffset = 0;
int endPos = ts.positionAtColumn(text, m_blockSelection.lastVisualColumn, &endOffset);
cursor.setPosition(block.position() + qMin(block.length()-1, firstColumn));
cursor.setPosition(block.position() + qMin(block.length()-1, lastColumn), QTextCursor::KeepAnchor);
cursor.setPosition(block.position() + startPos);
cursor.setPosition(block.position() + endPos, QTextCursor::KeepAnchor);
cursor.removeSelectedText();
if (block == endBlock)
if (startOffset < 0)
cursor.insertText(QString(ts.m_tabSize + startOffset, QLatin1Char(' ')));
if (endOffset < 0)
cursor.insertText(QString(-endOffset, QLatin1Char(' ')));
if (block == lastBlock)
break;
block = block.next();
}
cursor.setPosition(start);
cursor.setPosition(cursorPosition);
if (!text.isEmpty())
cursor.insertText(text);
cursor.endEditBlock();
......@@ -2448,37 +2428,25 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
}
}
if (!d->m_findScopeStart.isNull()) {
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn < 0) {
TextEditorOverlay *overlay = new TextEditorOverlay(this);
overlay->addOverlaySelection(d->m_findScopeStart.position(),
d->m_findScopeEnd.position(),
d->m_searchScopeFormat.background().color().darker(120),
d->m_searchScopeFormat.background().color(),
TextEditorOverlay::ExpandBegin,
d->m_findScopeVerticalBlockSelection);
TextEditorOverlay::ExpandBegin);
overlay->setAlpha(false);
overlay->paint(&painter, e->rect());
delete overlay;
}
BlockSelectionData *blockSelection = 0;
int blockSelectionIndex = -1;
if (d->m_inBlockSelectionMode
&& context.selections.count() && context.selections.last().cursor == textCursor()) {
blockSelection = new BlockSelectionData;
blockSelection->selectionIndex = context.selections.size()-1;
const QAbstractTextDocumentLayout::Selection &selection = context.selections[blockSelection->selectionIndex];
int start = blockSelection->selectionStart = selection.cursor.selectionStart();
int end = blockSelection->selectionEnd = selection.cursor.selectionEnd();
QTextBlock block = doc->findBlock(start);
int columnA = start - block.position();
block = doc->findBlock(end);
int columnB = end - block.position();
blockSelection->firstColumn = qMin(columnA, columnB);
blockSelection->lastColumn = qMax(columnA, columnB) + d->m_blockSelectionExtraX;
blockSelectionIndex = context.selections.size()-1;
context.selections[blockSelectionIndex].format.clearBackground();
}
QTextBlock visibleCollapsedBlock;
......@@ -2517,6 +2485,74 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
} // end first pass
// possible extra pass for the block selection find scope
if (!d->m_findScopeStart.isNull() && d->m_findScopeVerticalBlockSelectionFirstColumn >= 0) {
QTextBlock blockFS = block;
QPointF offsetFS = offset;
while (blockFS.isValid()) {
QRectF r = blockBoundingRect(blockFS).translated(offsetFS);
if (r.bottom() >= er.top() && r.top() <= er.bottom()) {
if (blockFS.position() >= d->m_findScopeStart.block().position()
&& blockFS.position() <= d->m_findScopeEnd.block().position()) {
QTextLayout *layout = blockFS.layout();
QString text = blockFS.text();
const TabSettings &ts = tabSettings();
int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
int offset = 0;
int relativePos = ts.positionAtColumn(text,
d->m_findScopeVerticalBlockSelectionFirstColumn,
&offset);
QTextLine line = layout->lineForTextPosition(relativePos);
qreal x = line.cursorToX(relativePos) + offset * spacew;
int eoffset = 0;
int erelativePos = ts.positionAtColumn(text,
d->m_findScopeVerticalBlockSelectionLastColumn,
&eoffset);
QTextLine eline = layout->lineForTextPosition(erelativePos);
qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
QRectF rr = line.naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
if (line.lineNumber() == eline.lineNumber()) {
rr.setRight(r.left() + ex);
}
painter.fillRect(rr, d->m_searchScopeFormat.background());
QColor lineCol = d->m_searchScopeFormat.background().color().darker(120);
QPen pen = painter.pen();
painter.setPen(lineCol);
if (blockFS == d->m_findScopeStart.block())
painter.drawLine(rr.topLeft(), rr.topRight());
if (blockFS == d->m_findScopeEnd.block())
painter.drawLine(rr.bottomLeft(), rr.bottomRight());
painter.drawLine(rr.topLeft(), rr.bottomLeft());
painter.drawLine(rr.topRight(), rr.bottomRight());
painter.setPen(pen);
}
}
offsetFS.ry() += r.height();
if (offsetFS.y() > viewportRect.height())
break;
blockFS = blockFS.next();
if (!blockFS.isVisible()) {
// invisible blocks do have zero line count
blockFS = doc->findBlockByLineNumber(blockFS.firstLineNumber());
}
}
}
d->m_searchResultOverlay->fill(&painter,
d->m_searchResultFormat.background().color(),
e->rect());
......@@ -2565,9 +2601,11 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
o.start = selStart;
o.length = selEnd - selStart;
o.format = range.format;
if (blockSelection && blockSelection->selectionIndex == i) {
o.start = qMin(blockSelection->firstColumn, bllen-1);
o.length = qMin(blockSelection->lastColumn, bllen-1) - o.start;
if (i == blockSelectionIndex) {
QString text = block.text();
const TabSettings &ts = tabSettings();
o.start = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn);
o.length = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn) - o.start;
}
if ((hasMainSelection && i == context.selections.size()-1)
|| (o.format.foreground().style() == Qt::NoBrush
......@@ -2609,6 +2647,62 @@ void BaseTextEditor::paintEvent(QPaintEvent *e)
painter.fillRect(rr, color);
}
QRectF blockSelectionCursorRect;
if (d->m_inBlockSelectionMode
&& block.position() >= d->m_blockSelection.firstBlock.block().position()
&& block.position() <= d->m_blockSelection.lastBlock.block().position()) {
QString text = block.text();
const TabSettings &ts = tabSettings();
int spacew = fontMetrics().width(QChar(QLatin1Char(' ')));
int offset = 0;
int relativePos = ts.positionAtColumn(text, d->m_blockSelection.firstVisualColumn, &offset);
QTextLine line = layout->lineForTextPosition(relativePos);
qreal x = line.cursorToX(relativePos) + offset * spacew;
int eoffset = 0;
int erelativePos = ts.positionAtColumn(text, d->m_blockSelection.lastVisualColumn, &eoffset);
QTextLine eline = layout->lineForTextPosition(erelativePos);
qreal ex = eline.cursorToX(erelativePos) + eoffset * spacew;
QRectF rr = line.naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
if (line.lineNumber() == eline.lineNumber()) {
rr.setRight(r.left() + ex);
}
painter.fillRect(rr, palette().highlight());
if ((d->m_blockSelection.anchor == BaseTextBlockSelection::TopLeft
&& block == d->m_blockSelection.firstBlock.block())
|| (d->m_blockSelection.anchor == BaseTextBlockSelection::BottomLeft
&& block == d->m_blockSelection.lastBlock.block())
) {
rr.setRight(rr.left()+2);
blockSelectionCursorRect = rr;
}
for (int i = line.lineNumber() + 1; i < eline.lineNumber(); ++i) {
rr = layout->lineAt(i).naturalTextRect();
rr.moveTop(rr.top() + r.top());
rr.setLeft(r.left() + x);
painter.fillRect(rr, palette().highlight());
}
rr = eline.naturalTextRect();