Support visual cursor movement for BIDI text
Bidi input can in some contexts be more intuitive if the cursor works in visual way: pressing left arrow key always make cursor move one character to the left regardless the language of text, pressing right arrow key always make cursor move to the right. It is also the behavior of Mac OS X. Based on the above reason and requests from Symbian we implemented this support for visual movement in BIDI text. 3 public properties are added to QTextDocument, QTextLayout and QLineEdit respectively: - QTextDocument::defaultCursorMoveStyle can be used to control the cursor behavior in all widgets based on QTextDocument, like QTextEdit, QPlainTextEdit, etc. When set to QTextCursor:: Visual, it will enable visual movement for all the cursors in the corresponding text edit. Default is QTextCursor::Logical. - QTextLayout::cursorMoveStyle is used for low-level cursor manipulation. When set to Visual, it will enable visual movement behavior for all the cursor related methods, including cursorToX, xToCursor and drawCursor. Default is Logical. - QLineEdit::cursorMoveStyle is used to control cursor movement behavior in QLineEdit. Default is Logical.: Task-number: QTBUG-13859 Reviewed-by: Eskil (cherry picked from commit c480dd641f5d22d1ee72cb27bf39e24c6df65658)
This commit is contained in:
parent
028aa80d70
commit
0f7cba14f6
@ -362,20 +362,23 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
|
|||||||
currentCharFormat = -1;
|
currentCharFormat = -1;
|
||||||
bool adjustX = true;
|
bool adjustX = true;
|
||||||
QTextBlock blockIt = block();
|
QTextBlock blockIt = block();
|
||||||
|
bool visualMovement = priv->defaultCursorMoveStyle == QTextCursor::Visual;
|
||||||
|
|
||||||
if (!blockIt.isValid())
|
if (!blockIt.isValid())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (op >= QTextCursor::Left && op <= QTextCursor::WordRight
|
if (blockIt.textDirection() == Qt::RightToLeft) {
|
||||||
&& blockIt.textDirection() == Qt::RightToLeft) {
|
if (op == QTextCursor::WordLeft)
|
||||||
|
op = QTextCursor::NextWord;
|
||||||
|
else if (op == QTextCursor::WordRight)
|
||||||
|
op = QTextCursor::PreviousWord;
|
||||||
|
|
||||||
|
if (!visualMovement) {
|
||||||
if (op == QTextCursor::Left)
|
if (op == QTextCursor::Left)
|
||||||
op = QTextCursor::NextCharacter;
|
op = QTextCursor::NextCharacter;
|
||||||
else if (op == QTextCursor::Right)
|
else if (op == QTextCursor::Right)
|
||||||
op = QTextCursor::PreviousCharacter;
|
op = QTextCursor::PreviousCharacter;
|
||||||
else if (op == QTextCursor::WordLeft)
|
}
|
||||||
op = QTextCursor::NextWord;
|
|
||||||
else if (op == QTextCursor::WordRight)
|
|
||||||
op = QTextCursor::PreviousWord;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QTextLayout *layout = blockLayout(blockIt);
|
const QTextLayout *layout = blockLayout(blockIt);
|
||||||
@ -418,9 +421,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTextCursor::PreviousCharacter:
|
case QTextCursor::PreviousCharacter:
|
||||||
case QTextCursor::Left:
|
|
||||||
newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
|
newPosition = priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
|
||||||
break;
|
break;
|
||||||
|
case QTextCursor::Left:
|
||||||
|
newPosition = visualMovement ? priv->leftCursorPosition(position)
|
||||||
|
: priv->previousCursorPosition(position, QTextLayout::SkipCharacters);
|
||||||
|
break;
|
||||||
case QTextCursor::StartOfWord: {
|
case QTextCursor::StartOfWord: {
|
||||||
if (relativePos == 0)
|
if (relativePos == 0)
|
||||||
break;
|
break;
|
||||||
@ -529,9 +535,12 @@ bool QTextCursorPrivate::movePosition(QTextCursor::MoveOperation op, QTextCursor
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case QTextCursor::NextCharacter:
|
case QTextCursor::NextCharacter:
|
||||||
case QTextCursor::Right:
|
|
||||||
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
|
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
|
||||||
break;
|
break;
|
||||||
|
case QTextCursor::Right:
|
||||||
|
newPosition = visualMovement ? priv->rightCursorPosition(position)
|
||||||
|
: priv->nextCursorPosition(position, QTextLayout::SkipCharacters);
|
||||||
|
break;
|
||||||
case QTextCursor::NextWord:
|
case QTextCursor::NextWord:
|
||||||
case QTextCursor::WordRight:
|
case QTextCursor::WordRight:
|
||||||
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
|
newPosition = priv->nextCursorPosition(position, QTextLayout::SkipWords);
|
||||||
@ -2558,4 +2567,19 @@ QTextDocument *QTextCursor::document() const
|
|||||||
return 0; // document went away
|
return 0; // document went away
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\enum QTextCursor::MoveStyle
|
||||||
|
|
||||||
|
This enum describes the movement style available to QTextCursor. The options
|
||||||
|
are:
|
||||||
|
|
||||||
|
\value Logical Within a left-to-right text block, increase cursor position
|
||||||
|
when pressing left arrow key, decrease cursor position when pressing the
|
||||||
|
right arrow key. If the text block is right-to-left, the opposite behavior
|
||||||
|
applies.
|
||||||
|
\value Visual Pressing the left arrow key will always cause the cursor to move
|
||||||
|
left, regardless of the text's writing direction. The same behavior applies to
|
||||||
|
right arrow key.
|
||||||
|
*/
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -86,6 +86,10 @@ public:
|
|||||||
MoveAnchor,
|
MoveAnchor,
|
||||||
KeepAnchor
|
KeepAnchor
|
||||||
};
|
};
|
||||||
|
enum MoveStyle {
|
||||||
|
Logical,
|
||||||
|
Visual,
|
||||||
|
};
|
||||||
|
|
||||||
void setPosition(int pos, MoveMode mode = MoveAnchor);
|
void setPosition(int pos, MoveMode mode = MoveAnchor);
|
||||||
int position() const;
|
int position() const;
|
||||||
|
@ -585,6 +585,29 @@ void QTextDocument::setDefaultTextOption(const QTextOption &option)
|
|||||||
d->lout->documentChanged(0, 0, d->length());
|
d->lout->documentChanged(0, 0, d->length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 4.8
|
||||||
|
|
||||||
|
The default cursor movement style is used by all QTextCursor objects
|
||||||
|
created from the document. The default is QTextCursor::Logical.
|
||||||
|
*/
|
||||||
|
QTextCursor::MoveStyle QTextDocument::defaultCursorMoveStyle() const
|
||||||
|
{
|
||||||
|
Q_D(const QTextDocument);
|
||||||
|
return d->defaultCursorMoveStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\since 4.8
|
||||||
|
|
||||||
|
Set the default cursor movement style.
|
||||||
|
*/
|
||||||
|
void QTextDocument::setDefaultCursorMoveStyle(QTextCursor::MoveStyle style)
|
||||||
|
{
|
||||||
|
Q_D(QTextDocument);
|
||||||
|
d->defaultCursorMoveStyle = style;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\fn void QTextDocument::markContentsDirty(int position, int length)
|
\fn void QTextDocument::markContentsDirty(int position, int length)
|
||||||
|
|
||||||
|
@ -46,6 +46,7 @@
|
|||||||
#include <QtCore/qsize.h>
|
#include <QtCore/qsize.h>
|
||||||
#include <QtCore/qrect.h>
|
#include <QtCore/qrect.h>
|
||||||
#include <QtGui/qfont.h>
|
#include <QtGui/qfont.h>
|
||||||
|
#include <QtGui/qtextcursor.h>
|
||||||
|
|
||||||
QT_BEGIN_HEADER
|
QT_BEGIN_HEADER
|
||||||
|
|
||||||
@ -60,7 +61,6 @@ class QPainter;
|
|||||||
class QPrinter;
|
class QPrinter;
|
||||||
class QAbstractTextDocumentLayout;
|
class QAbstractTextDocumentLayout;
|
||||||
class QPoint;
|
class QPoint;
|
||||||
class QTextCursor;
|
|
||||||
class QTextObject;
|
class QTextObject;
|
||||||
class QTextFormat;
|
class QTextFormat;
|
||||||
class QTextFrame;
|
class QTextFrame;
|
||||||
@ -269,6 +269,9 @@ public:
|
|||||||
QTextOption defaultTextOption() const;
|
QTextOption defaultTextOption() const;
|
||||||
void setDefaultTextOption(const QTextOption &option);
|
void setDefaultTextOption(const QTextOption &option);
|
||||||
|
|
||||||
|
QTextCursor::MoveStyle defaultCursorMoveStyle() const;
|
||||||
|
void setDefaultCursorMoveStyle(QTextCursor::MoveStyle style);
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void contentsChange(int from, int charsRemoves, int charsAdded);
|
void contentsChange(int from, int charsRemoves, int charsAdded);
|
||||||
void contentsChanged();
|
void contentsChanged();
|
||||||
|
@ -209,6 +209,7 @@ QTextDocumentPrivate::QTextDocumentPrivate()
|
|||||||
|
|
||||||
defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
|
defaultTextOption.setTabStop(80); // same as in qtextengine.cpp
|
||||||
defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
defaultTextOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||||
|
defaultCursorMoveStyle = QTextCursor::Logical;
|
||||||
|
|
||||||
indentWidth = 40;
|
indentWidth = 40;
|
||||||
documentMargin = 4;
|
documentMargin = 4;
|
||||||
@ -1382,6 +1383,20 @@ int QTextDocumentPrivate::previousCursorPosition(int position, QTextLayout::Curs
|
|||||||
return it.layout()->previousCursorPosition(position-start, mode) + start;
|
return it.layout()->previousCursorPosition(position-start, mode) + start;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int QTextDocumentPrivate::leftCursorPosition(int position) const
|
||||||
|
{
|
||||||
|
QTextBlock it = blocksFind(position);
|
||||||
|
int start = it.position();
|
||||||
|
return it.layout()->leftCursorPosition(position-start) + start;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextDocumentPrivate::rightCursorPosition(int position) const
|
||||||
|
{
|
||||||
|
QTextBlock it = blocksFind(position);
|
||||||
|
int start = it.position();
|
||||||
|
return it.layout()->rightCursorPosition(position-start) + start;
|
||||||
|
}
|
||||||
|
|
||||||
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
|
void QTextDocumentPrivate::changeObjectFormat(QTextObject *obj, int format)
|
||||||
{
|
{
|
||||||
beginEditBlock();
|
beginEditBlock();
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
#include "private/qtextformat_p.h"
|
#include "private/qtextformat_p.h"
|
||||||
#include "QtGui/qtextdocument.h"
|
#include "QtGui/qtextdocument.h"
|
||||||
#include "QtGui/qtextobject.h"
|
#include "QtGui/qtextobject.h"
|
||||||
|
#include "QtGui/qtextcursor.h"
|
||||||
#include "QtCore/qmap.h"
|
#include "QtCore/qmap.h"
|
||||||
#include "QtCore/qvariant.h"
|
#include "QtCore/qvariant.h"
|
||||||
#include "QtCore/qurl.h"
|
#include "QtCore/qurl.h"
|
||||||
@ -244,6 +245,8 @@ public:
|
|||||||
|
|
||||||
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
|
int nextCursorPosition(int position, QTextLayout::CursorMode mode) const;
|
||||||
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
|
int previousCursorPosition(int position, QTextLayout::CursorMode mode) const;
|
||||||
|
int leftCursorPosition(int position) const;
|
||||||
|
int rightCursorPosition(int position) const;
|
||||||
|
|
||||||
void changeObjectFormat(QTextObject *group, int format);
|
void changeObjectFormat(QTextObject *group, int format);
|
||||||
|
|
||||||
@ -339,6 +342,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
QTextOption defaultTextOption;
|
QTextOption defaultTextOption;
|
||||||
|
QTextCursor::MoveStyle defaultCursorMoveStyle;
|
||||||
#ifndef QT_NO_CSSPARSER
|
#ifndef QT_NO_CSSPARSER
|
||||||
QCss::StyleSheet parsedDefaultStyleSheet;
|
QCss::StyleSheet parsedDefaultStyleSheet;
|
||||||
#endif
|
#endif
|
||||||
|
@ -1304,6 +1304,7 @@ static void init(QTextEngine *e)
|
|||||||
e->ignoreBidi = false;
|
e->ignoreBidi = false;
|
||||||
e->cacheGlyphs = false;
|
e->cacheGlyphs = false;
|
||||||
e->forceJustification = false;
|
e->forceJustification = false;
|
||||||
|
e->visualMovement = false;
|
||||||
|
|
||||||
e->layoutData = 0;
|
e->layoutData = 0;
|
||||||
|
|
||||||
@ -2737,6 +2738,180 @@ QFixed QTextEngine::leadingSpaceWidth(const QScriptLine &line)
|
|||||||
return width(line.from + pos, line.length - pos);
|
return width(line.from + pos, line.length - pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QFixed QTextEngine::alignLine(const QScriptLine &line)
|
||||||
|
{
|
||||||
|
QFixed x = 0;
|
||||||
|
justify(line);
|
||||||
|
// if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
|
||||||
|
if (!line.justified && line.width != QFIXED_MAX) {
|
||||||
|
int align = option.alignment();
|
||||||
|
if (align & Qt::AlignJustify && isRightToLeft())
|
||||||
|
align = Qt::AlignRight;
|
||||||
|
if (align & Qt::AlignRight)
|
||||||
|
x = line.width - (line.textAdvance + leadingSpaceWidth(line));
|
||||||
|
else if (align & Qt::AlignHCenter)
|
||||||
|
x = (line.width - line.textAdvance)/2;
|
||||||
|
}
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFixed QTextEngine::offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos)
|
||||||
|
{
|
||||||
|
unsigned short *logClusters = this->logClusters(si);
|
||||||
|
const QGlyphLayout &glyphs = shapedGlyphs(si);
|
||||||
|
|
||||||
|
int offsetInCluster = 0;
|
||||||
|
for (int i = pos - 1; i >= 0; i--) {
|
||||||
|
if (logClusters[i] == glyph_pos)
|
||||||
|
offsetInCluster++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// in the case that the offset is inside a (multi-character) glyph,
|
||||||
|
// interpolate the position.
|
||||||
|
if (offsetInCluster > 0) {
|
||||||
|
int clusterLength = 0;
|
||||||
|
for (int i = pos - offsetInCluster; i < max; i++) {
|
||||||
|
if (logClusters[i] == glyph_pos)
|
||||||
|
clusterLength++;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (clusterLength)
|
||||||
|
return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::previousLogicalPosition(int oldPos) const
|
||||||
|
{
|
||||||
|
const HB_CharAttributes *attrs = attributes();
|
||||||
|
if (!attrs || oldPos < 0)
|
||||||
|
return oldPos;
|
||||||
|
|
||||||
|
if (oldPos <= 0)
|
||||||
|
return 0;
|
||||||
|
oldPos--;
|
||||||
|
while (oldPos && !attrs[oldPos].charStop)
|
||||||
|
oldPos--;
|
||||||
|
return oldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::nextLogicalPosition(int oldPos) const
|
||||||
|
{
|
||||||
|
const HB_CharAttributes *attrs = attributes();
|
||||||
|
int len = block.isValid() ? block.length() - 1
|
||||||
|
: layoutData->string.length();
|
||||||
|
Q_ASSERT(len <= layoutData->string.length());
|
||||||
|
if (!attrs || oldPos < 0 || oldPos >= len)
|
||||||
|
return oldPos;
|
||||||
|
|
||||||
|
oldPos++;
|
||||||
|
while (oldPos < len && !attrs[oldPos].charStop)
|
||||||
|
oldPos++;
|
||||||
|
return oldPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::lineNumberForTextPosition(int pos)
|
||||||
|
{
|
||||||
|
if (!layoutData)
|
||||||
|
itemize();
|
||||||
|
if (pos == layoutData->string.length() && lines.size())
|
||||||
|
return lines.size() - 1;
|
||||||
|
for (int i = 0; i < lines.size(); ++i) {
|
||||||
|
const QScriptLine& line = lines[i];
|
||||||
|
if (line.from + line.length > pos)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void QTextEngine::insertionPointsForLine(int lineNum, QVector<int> &insertionPoints)
|
||||||
|
{
|
||||||
|
QTextLineItemIterator iterator(this, lineNum);
|
||||||
|
bool rtl = isRightToLeft();
|
||||||
|
bool lastLine = lineNum >= lines.size() - 1;
|
||||||
|
|
||||||
|
while (!iterator.atEnd()) {
|
||||||
|
iterator.next();
|
||||||
|
const QScriptItem *si = &layoutData->items[iterator.item];
|
||||||
|
if (si->analysis.bidiLevel % 2) {
|
||||||
|
int i = iterator.itemEnd - 1, min = iterator.itemStart;
|
||||||
|
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
|
||||||
|
i++;
|
||||||
|
for (; i >= min; i--)
|
||||||
|
insertionPoints.push_back(i);
|
||||||
|
} else {
|
||||||
|
int i = iterator.itemStart, max = iterator.itemEnd;
|
||||||
|
if (lastLine && (rtl ? iterator.atBeginning() : iterator.atEnd()))
|
||||||
|
max++;
|
||||||
|
for (; i < max; i++)
|
||||||
|
insertionPoints.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::endOfLine(int lineNum)
|
||||||
|
{
|
||||||
|
QVector<int> insertionPoints;
|
||||||
|
insertionPointsForLine(lineNum, insertionPoints);
|
||||||
|
|
||||||
|
if (insertionPoints.size() > 0)
|
||||||
|
return insertionPoints.last();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::beginningOfLine(int lineNum)
|
||||||
|
{
|
||||||
|
QVector<int> insertionPoints;
|
||||||
|
insertionPointsForLine(lineNum, insertionPoints);
|
||||||
|
|
||||||
|
if (insertionPoints.size() > 0)
|
||||||
|
return insertionPoints.first();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int QTextEngine::positionAfterVisualMovement(int pos, QTextCursor::MoveOperation op)
|
||||||
|
{
|
||||||
|
if (!layoutData)
|
||||||
|
itemize();
|
||||||
|
|
||||||
|
bool moveRight = (op == QTextCursor::Right);
|
||||||
|
bool alignRight = isRightToLeft();
|
||||||
|
if (!layoutData->hasBidi)
|
||||||
|
return moveRight ^ alignRight ? nextLogicalPosition(pos) : previousLogicalPosition(pos);
|
||||||
|
|
||||||
|
int lineNum = lineNumberForTextPosition(pos);
|
||||||
|
Q_ASSERT(lineNum >= 0);
|
||||||
|
|
||||||
|
QVector<int> insertionPoints;
|
||||||
|
insertionPointsForLine(lineNum, insertionPoints);
|
||||||
|
int i, max = insertionPoints.size();
|
||||||
|
for (i = 0; i < max; i++)
|
||||||
|
if (pos == insertionPoints[i]) {
|
||||||
|
if (moveRight) {
|
||||||
|
if (i + 1 < max)
|
||||||
|
return insertionPoints[i + 1];
|
||||||
|
} else {
|
||||||
|
if (i > 0)
|
||||||
|
return insertionPoints[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (moveRight ^ alignRight) {
|
||||||
|
if (lineNum + 1 < lines.size())
|
||||||
|
return alignRight ? endOfLine(lineNum + 1) : beginningOfLine(lineNum + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (lineNum > 0)
|
||||||
|
return alignRight ? beginningOfLine(lineNum - 1) : endOfLine(lineNum - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
|
QStackTextEngine::QStackTextEngine(const QString &string, const QFont &f)
|
||||||
: QTextEngine(string, f),
|
: QTextEngine(string, f),
|
||||||
_layoutData(string, _memory, MemSize)
|
_layoutData(string, _memory, MemSize)
|
||||||
@ -2841,5 +3016,127 @@ glyph_metrics_t glyph_metrics_t::transformed(const QTransform &matrix) const
|
|||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int _lineNum, const QPointF &pos,
|
||||||
|
const QTextLayout::FormatRange *_selection)
|
||||||
|
: eng(_eng),
|
||||||
|
line(eng->lines[_lineNum]),
|
||||||
|
si(0),
|
||||||
|
lineNum(_lineNum),
|
||||||
|
lineEnd(line.from + line.length),
|
||||||
|
firstItem(eng->findItem(line.from)),
|
||||||
|
lastItem(eng->findItem(lineEnd - 1)),
|
||||||
|
nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
|
||||||
|
logicalItem(-1),
|
||||||
|
item(-1),
|
||||||
|
visualOrder(nItems),
|
||||||
|
levels(nItems),
|
||||||
|
selection(_selection)
|
||||||
|
{
|
||||||
|
pos_x = x = QFixed::fromReal(pos.x());
|
||||||
|
|
||||||
|
x += line.x;
|
||||||
|
|
||||||
|
x += eng->alignLine(line);
|
||||||
|
|
||||||
|
for (int i = 0; i < nItems; ++i)
|
||||||
|
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
|
||||||
|
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
|
||||||
|
|
||||||
|
eng->shapeLine(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
QScriptItem &QTextLineItemIterator::next()
|
||||||
|
{
|
||||||
|
x += itemWidth;
|
||||||
|
|
||||||
|
++logicalItem;
|
||||||
|
item = visualOrder[logicalItem] + firstItem;
|
||||||
|
itemLength = eng->length(item);
|
||||||
|
si = &eng->layoutData->items[item];
|
||||||
|
if (!si->num_glyphs)
|
||||||
|
eng->shape(item);
|
||||||
|
|
||||||
|
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
||||||
|
itemWidth = si->width;
|
||||||
|
return *si;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned short *logClusters = eng->logClusters(si);
|
||||||
|
QGlyphLayout glyphs = eng->shapedGlyphs(si);
|
||||||
|
|
||||||
|
itemStart = qMax(line.from, si->position);
|
||||||
|
glyphsStart = logClusters[itemStart - si->position];
|
||||||
|
if (lineEnd < si->position + itemLength) {
|
||||||
|
itemEnd = lineEnd;
|
||||||
|
glyphsEnd = logClusters[itemEnd-si->position];
|
||||||
|
} else {
|
||||||
|
itemEnd = si->position + itemLength;
|
||||||
|
glyphsEnd = si->num_glyphs;
|
||||||
|
}
|
||||||
|
// show soft-hyphen at line-break
|
||||||
|
if (si->position + itemLength >= lineEnd
|
||||||
|
&& eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
|
||||||
|
glyphs.attributes[glyphsEnd - 1].dontPrint = false;
|
||||||
|
|
||||||
|
itemWidth = 0;
|
||||||
|
for (int g = glyphsStart; g < glyphsEnd; ++g)
|
||||||
|
itemWidth += glyphs.effectiveAdvance(g);
|
||||||
|
|
||||||
|
return *si;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
|
||||||
|
{
|
||||||
|
*selectionX = *selectionWidth = 0;
|
||||||
|
|
||||||
|
if (!selection)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
||||||
|
if (si->position >= selection->start + selection->length
|
||||||
|
|| si->position + itemLength <= selection->start)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*selectionX = x;
|
||||||
|
*selectionWidth = itemWidth;
|
||||||
|
} else {
|
||||||
|
unsigned short *logClusters = eng->logClusters(si);
|
||||||
|
QGlyphLayout glyphs = eng->shapedGlyphs(si);
|
||||||
|
|
||||||
|
int from = qMax(itemStart, selection->start) - si->position;
|
||||||
|
int to = qMin(itemEnd, selection->start + selection->length) - si->position;
|
||||||
|
if (from >= to)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int start_glyph = logClusters[from];
|
||||||
|
int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
|
||||||
|
QFixed soff;
|
||||||
|
QFixed swidth;
|
||||||
|
if (si->analysis.bidiLevel %2) {
|
||||||
|
for (int g = glyphsEnd - 1; g >= end_glyph; --g)
|
||||||
|
soff += glyphs.effectiveAdvance(g);
|
||||||
|
for (int g = end_glyph - 1; g >= start_glyph; --g)
|
||||||
|
swidth += glyphs.effectiveAdvance(g);
|
||||||
|
} else {
|
||||||
|
for (int g = glyphsStart; g < start_glyph; ++g)
|
||||||
|
soff += glyphs.effectiveAdvance(g);
|
||||||
|
for (int g = start_glyph; g < end_glyph; ++g)
|
||||||
|
swidth += glyphs.effectiveAdvance(g);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the starting character is in the middle of a ligature,
|
||||||
|
// selection should only contain the right part of that ligature
|
||||||
|
// glyph, so we need to get the width of the left part here and
|
||||||
|
// add it to *selectionX
|
||||||
|
QFixed leftOffsetInLigature = eng->offsetInLigature(si, from, to, start_glyph);
|
||||||
|
*selectionX = x + soff + leftOffsetInLigature;
|
||||||
|
*selectionWidth = swidth - leftOffsetInLigature;
|
||||||
|
// If the ending character is also part of a ligature, swidth does
|
||||||
|
// not contain that part yet, we also need to find out the width of
|
||||||
|
// that left part
|
||||||
|
*selectionWidth += eng->offsetInLigature(si, to, eng->length(item), end_glyph);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -64,6 +64,7 @@
|
|||||||
#include "QtGui/qpaintengine.h"
|
#include "QtGui/qpaintengine.h"
|
||||||
#include "QtGui/qtextobject.h"
|
#include "QtGui/qtextobject.h"
|
||||||
#include "QtGui/qtextoption.h"
|
#include "QtGui/qtextoption.h"
|
||||||
|
#include "QtGui/qtextcursor.h"
|
||||||
#include "QtCore/qset.h"
|
#include "QtCore/qset.h"
|
||||||
#include "QtCore/qdebug.h"
|
#include "QtCore/qdebug.h"
|
||||||
#ifndef QT_BUILD_COMPAT_LIB
|
#ifndef QT_BUILD_COMPAT_LIB
|
||||||
@ -471,6 +472,7 @@ public:
|
|||||||
void shape(int item) const;
|
void shape(int item) const;
|
||||||
|
|
||||||
void justify(const QScriptLine &si);
|
void justify(const QScriptLine &si);
|
||||||
|
QFixed alignLine(const QScriptLine &line);
|
||||||
|
|
||||||
QFixed width(int charFrom, int numChars) const;
|
QFixed width(int charFrom, int numChars) const;
|
||||||
glyph_metrics_t boundingBox(int from, int len) const;
|
glyph_metrics_t boundingBox(int from, int len) const;
|
||||||
@ -586,12 +588,18 @@ public:
|
|||||||
uint cacheGlyphs : 1;
|
uint cacheGlyphs : 1;
|
||||||
uint stackEngine : 1;
|
uint stackEngine : 1;
|
||||||
uint forceJustification : 1;
|
uint forceJustification : 1;
|
||||||
|
uint visualMovement : 1;
|
||||||
|
|
||||||
int *underlinePositions;
|
int *underlinePositions;
|
||||||
|
|
||||||
mutable LayoutData *layoutData;
|
mutable LayoutData *layoutData;
|
||||||
|
|
||||||
inline bool hasFormats() const { return (block.docHandle() || specialData); }
|
inline bool hasFormats() const { return (block.docHandle() || specialData); }
|
||||||
|
inline bool visualCursorMovement() const
|
||||||
|
{
|
||||||
|
return (visualMovement ||
|
||||||
|
(block.docHandle() ? block.docHandle()->defaultCursorMoveStyle == QTextCursor::Visual : false));
|
||||||
|
}
|
||||||
|
|
||||||
struct SpecialData {
|
struct SpecialData {
|
||||||
int preeditPosition;
|
int preeditPosition;
|
||||||
@ -611,6 +619,13 @@ public:
|
|||||||
void shapeLine(const QScriptLine &line);
|
void shapeLine(const QScriptLine &line);
|
||||||
QFixed leadingSpaceWidth(const QScriptLine &line);
|
QFixed leadingSpaceWidth(const QScriptLine &line);
|
||||||
|
|
||||||
|
QFixed offsetInLigature(const QScriptItem *si, int pos, int max, int glyph_pos);
|
||||||
|
int previousLogicalPosition(int oldPos) const;
|
||||||
|
int nextLogicalPosition(int oldPos) const;
|
||||||
|
int lineNumberForTextPosition(int pos);
|
||||||
|
int positionAfterVisualMovement(int oldPos, QTextCursor::MoveOperation op);
|
||||||
|
void insertionPointsForLine(int lineNum, QVector<int> &insertionPoints);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setBoundary(int strPos) const;
|
void setBoundary(int strPos) const;
|
||||||
void addRequiredBoundaries() const;
|
void addRequiredBoundaries() const;
|
||||||
@ -625,6 +640,8 @@ private:
|
|||||||
void splitItem(int item, int pos) const;
|
void splitItem(int item, int pos) const;
|
||||||
|
|
||||||
void resolveAdditionalFormats() const;
|
void resolveAdditionalFormats() const;
|
||||||
|
int endOfLine(int lineNum);
|
||||||
|
int beginningOfLine(int lineNum);
|
||||||
};
|
};
|
||||||
|
|
||||||
class QStackTextEngine : public QTextEngine {
|
class QStackTextEngine : public QTextEngine {
|
||||||
@ -635,6 +652,49 @@ public:
|
|||||||
void *_memory[MemSize];
|
void *_memory[MemSize];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct QTextLineItemIterator
|
||||||
|
{
|
||||||
|
QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
|
||||||
|
const QTextLayout::FormatRange *_selection = 0);
|
||||||
|
|
||||||
|
inline bool atEnd() const { return logicalItem >= nItems - 1; }
|
||||||
|
inline bool atBeginning() const { return logicalItem <= 0; }
|
||||||
|
QScriptItem &next();
|
||||||
|
|
||||||
|
bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
|
||||||
|
inline bool isOutsideSelection() const {
|
||||||
|
QFixed tmp1, tmp2;
|
||||||
|
return !getSelectionBounds(&tmp1, &tmp2);
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextEngine *eng;
|
||||||
|
|
||||||
|
QFixed x;
|
||||||
|
QFixed pos_x;
|
||||||
|
const QScriptLine &line;
|
||||||
|
QScriptItem *si;
|
||||||
|
|
||||||
|
int lineNum;
|
||||||
|
int lineEnd;
|
||||||
|
int firstItem;
|
||||||
|
int lastItem;
|
||||||
|
int nItems;
|
||||||
|
int logicalItem;
|
||||||
|
int item;
|
||||||
|
int itemLength;
|
||||||
|
|
||||||
|
int glyphsStart;
|
||||||
|
int glyphsEnd;
|
||||||
|
int itemStart;
|
||||||
|
int itemEnd;
|
||||||
|
|
||||||
|
QFixed itemWidth;
|
||||||
|
|
||||||
|
QVarLengthArray<int> visualOrder;
|
||||||
|
QVarLengthArray<uchar> levels;
|
||||||
|
|
||||||
|
const QTextLayout::FormatRange *selection;
|
||||||
|
};
|
||||||
|
|
||||||
Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
|
Q_DECLARE_OPERATORS_FOR_FLAGS(QTextEngine::ShaperFlags)
|
||||||
|
|
||||||
|
@ -72,23 +72,6 @@ QT_BEGIN_NAMESPACE
|
|||||||
#define SuppressText 0x5012
|
#define SuppressText 0x5012
|
||||||
#define SuppressBackground 0x513
|
#define SuppressBackground 0x513
|
||||||
|
|
||||||
static QFixed alignLine(QTextEngine *eng, const QScriptLine &line)
|
|
||||||
{
|
|
||||||
QFixed x = 0;
|
|
||||||
eng->justify(line);
|
|
||||||
// if width is QFIXED_MAX that means we used setNumColumns() and that implicitly makes this line left aligned.
|
|
||||||
if (!line.justified && line.width != QFIXED_MAX) {
|
|
||||||
int align = eng->option.alignment();
|
|
||||||
if (align & Qt::AlignJustify && eng->isRightToLeft())
|
|
||||||
align = Qt::AlignRight;
|
|
||||||
if (align & Qt::AlignRight)
|
|
||||||
x = line.width - (line.textAdvance + eng->leadingSpaceWidth(line));
|
|
||||||
else if (align & Qt::AlignHCenter)
|
|
||||||
x = (line.width - line.textAdvance)/2;
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\class QTextLayout::FormatRange
|
\class QTextLayout::FormatRange
|
||||||
\reentrant
|
\reentrant
|
||||||
@ -595,6 +578,30 @@ bool QTextLayout::cacheEnabled() const
|
|||||||
return d->cacheGlyphs;
|
return d->cacheGlyphs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Set the visual cursor movement style. If the QTextLayout is backed by
|
||||||
|
a document, you can ignore this and use the option in QTextDocument,
|
||||||
|
this option is for widgets like QLineEdit or custom widgets without
|
||||||
|
a QTextDocument. Default value is QTextCursor::Logical.
|
||||||
|
|
||||||
|
\sa setCursorMoveStyle()
|
||||||
|
*/
|
||||||
|
void QTextLayout::setCursorMoveStyle(QTextCursor::MoveStyle style)
|
||||||
|
{
|
||||||
|
d->visualMovement = style == QTextCursor::Visual ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
The cursor movement style of this QTextLayout. The default is
|
||||||
|
QTextCursor::Logical.
|
||||||
|
|
||||||
|
\sa setCursorMoveStyle()
|
||||||
|
*/
|
||||||
|
QTextCursor::MoveStyle QTextLayout::cursorMoveStyle() const
|
||||||
|
{
|
||||||
|
return d->visualMovement ? QTextCursor::Visual : QTextCursor::Logical;
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Begins the layout process.
|
Begins the layout process.
|
||||||
|
|
||||||
@ -718,6 +725,34 @@ int QTextLayout::previousCursorPosition(int oldPos, CursorMode mode) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
Returns the cursor position to the right of \a oldPos, next to it.
|
||||||
|
It's dependent on the visual position of characters, after bi-directional
|
||||||
|
reordering.
|
||||||
|
|
||||||
|
\sa leftCursorPosition(), nextCursorPosition()
|
||||||
|
*/
|
||||||
|
int QTextLayout::rightCursorPosition(int oldPos) const
|
||||||
|
{
|
||||||
|
int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Right);
|
||||||
|
// qDebug("%d -> %d", oldPos, newPos);
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Returns the cursor position to the left of \a oldPos, next to it.
|
||||||
|
It's dependent on the visual position of characters, after bi-directional
|
||||||
|
reordering.
|
||||||
|
|
||||||
|
\sa rightCursorPosition(), previousCursorPosition()
|
||||||
|
*/
|
||||||
|
int QTextLayout::leftCursorPosition(int oldPos) const
|
||||||
|
{
|
||||||
|
int newPos = d->positionAfterVisualMovement(oldPos, QTextCursor::Left);
|
||||||
|
// qDebug("%d -> %d", oldPos, newPos);
|
||||||
|
return newPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!/
|
||||||
Returns true if position \a pos is a valid cursor position.
|
Returns true if position \a pos is a valid cursor position.
|
||||||
|
|
||||||
In a Unicode context some positions in the text are not valid
|
In a Unicode context some positions in the text are not valid
|
||||||
@ -815,16 +850,8 @@ QTextLine QTextLayout::lineAt(int i) const
|
|||||||
*/
|
*/
|
||||||
QTextLine QTextLayout::lineForTextPosition(int pos) const
|
QTextLine QTextLayout::lineForTextPosition(int pos) const
|
||||||
{
|
{
|
||||||
for (int i = 0; i < d->lines.size(); ++i) {
|
int lineNum = d->lineNumberForTextPosition(pos);
|
||||||
const QScriptLine& line = d->lines[i];
|
return lineNum >= 0 ? lineAt(lineNum) : QTextLine();
|
||||||
if (line.from + (int)line.length > pos)
|
|
||||||
return QTextLine(i, d);
|
|
||||||
}
|
|
||||||
if (!d->layoutData)
|
|
||||||
d->itemize();
|
|
||||||
if (pos == d->layoutData->string.length() && d->lines.size())
|
|
||||||
return QTextLine(d->lines.size()-1, d);
|
|
||||||
return QTextLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
@ -919,201 +946,6 @@ void QTextLayout::setFlags(int flags)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct QTextLineItemIterator
|
|
||||||
{
|
|
||||||
QTextLineItemIterator(QTextEngine *eng, int lineNum, const QPointF &pos = QPointF(),
|
|
||||||
const QTextLayout::FormatRange *_selection = 0);
|
|
||||||
|
|
||||||
inline bool atEnd() const { return logicalItem >= nItems - 1; }
|
|
||||||
QScriptItem &next();
|
|
||||||
|
|
||||||
bool getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const;
|
|
||||||
inline bool isOutsideSelection() const {
|
|
||||||
QFixed tmp1, tmp2;
|
|
||||||
return !getSelectionBounds(&tmp1, &tmp2);
|
|
||||||
}
|
|
||||||
|
|
||||||
QTextEngine *eng;
|
|
||||||
|
|
||||||
QFixed x;
|
|
||||||
QFixed pos_x;
|
|
||||||
const QScriptLine &line;
|
|
||||||
QScriptItem *si;
|
|
||||||
|
|
||||||
int lineEnd;
|
|
||||||
int firstItem;
|
|
||||||
int lastItem;
|
|
||||||
int nItems;
|
|
||||||
int logicalItem;
|
|
||||||
int item;
|
|
||||||
int itemLength;
|
|
||||||
|
|
||||||
int glyphsStart;
|
|
||||||
int glyphsEnd;
|
|
||||||
int itemStart;
|
|
||||||
int itemEnd;
|
|
||||||
|
|
||||||
QFixed itemWidth;
|
|
||||||
|
|
||||||
QVarLengthArray<int> visualOrder;
|
|
||||||
QVarLengthArray<uchar> levels;
|
|
||||||
|
|
||||||
const QTextLayout::FormatRange *selection;
|
|
||||||
};
|
|
||||||
|
|
||||||
QTextLineItemIterator::QTextLineItemIterator(QTextEngine *_eng, int lineNum, const QPointF &pos,
|
|
||||||
const QTextLayout::FormatRange *_selection)
|
|
||||||
: eng(_eng),
|
|
||||||
line(eng->lines[lineNum]),
|
|
||||||
si(0),
|
|
||||||
lineEnd(line.from + line.length),
|
|
||||||
firstItem(eng->findItem(line.from)),
|
|
||||||
lastItem(eng->findItem(lineEnd - 1)),
|
|
||||||
nItems((firstItem >= 0 && lastItem >= firstItem)? (lastItem-firstItem+1) : 0),
|
|
||||||
logicalItem(-1),
|
|
||||||
item(-1),
|
|
||||||
visualOrder(nItems),
|
|
||||||
levels(nItems),
|
|
||||||
selection(_selection)
|
|
||||||
{
|
|
||||||
pos_x = x = QFixed::fromReal(pos.x());
|
|
||||||
|
|
||||||
x += line.x;
|
|
||||||
|
|
||||||
x += alignLine(eng, line);
|
|
||||||
|
|
||||||
for (int i = 0; i < nItems; ++i)
|
|
||||||
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
|
|
||||||
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
|
|
||||||
|
|
||||||
eng->shapeLine(line);
|
|
||||||
}
|
|
||||||
|
|
||||||
QScriptItem &QTextLineItemIterator::next()
|
|
||||||
{
|
|
||||||
x += itemWidth;
|
|
||||||
|
|
||||||
++logicalItem;
|
|
||||||
item = visualOrder[logicalItem] + firstItem;
|
|
||||||
itemLength = eng->length(item);
|
|
||||||
si = &eng->layoutData->items[item];
|
|
||||||
if (!si->num_glyphs)
|
|
||||||
eng->shape(item);
|
|
||||||
|
|
||||||
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
|
||||||
itemWidth = si->width;
|
|
||||||
return *si;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned short *logClusters = eng->logClusters(si);
|
|
||||||
QGlyphLayout glyphs = eng->shapedGlyphs(si);
|
|
||||||
|
|
||||||
itemStart = qMax(line.from, si->position);
|
|
||||||
glyphsStart = logClusters[itemStart - si->position];
|
|
||||||
if (lineEnd < si->position + itemLength) {
|
|
||||||
itemEnd = lineEnd;
|
|
||||||
glyphsEnd = logClusters[itemEnd-si->position];
|
|
||||||
} else {
|
|
||||||
itemEnd = si->position + itemLength;
|
|
||||||
glyphsEnd = si->num_glyphs;
|
|
||||||
}
|
|
||||||
// show soft-hyphen at line-break
|
|
||||||
if (si->position + itemLength >= lineEnd
|
|
||||||
&& eng->layoutData->string.at(lineEnd - 1) == 0x00ad)
|
|
||||||
glyphs.attributes[glyphsEnd - 1].dontPrint = false;
|
|
||||||
|
|
||||||
itemWidth = 0;
|
|
||||||
for (int g = glyphsStart; g < glyphsEnd; ++g)
|
|
||||||
itemWidth += glyphs.effectiveAdvance(g);
|
|
||||||
|
|
||||||
return *si;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QFixed offsetInLigature(const unsigned short *logClusters,
|
|
||||||
const QGlyphLayout &glyphs,
|
|
||||||
int pos, int max, int glyph_pos)
|
|
||||||
{
|
|
||||||
int offsetInCluster = 0;
|
|
||||||
for (int i = pos - 1; i >= 0; i--) {
|
|
||||||
if (logClusters[i] == glyph_pos)
|
|
||||||
offsetInCluster++;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// in the case that the offset is inside a (multi-character) glyph,
|
|
||||||
// interpolate the position.
|
|
||||||
if (offsetInCluster > 0) {
|
|
||||||
int clusterLength = 0;
|
|
||||||
for (int i = pos - offsetInCluster; i < max; i++) {
|
|
||||||
if (logClusters[i] == glyph_pos)
|
|
||||||
clusterLength++;
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (clusterLength)
|
|
||||||
return glyphs.advances_x[glyph_pos] * offsetInCluster / clusterLength;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool QTextLineItemIterator::getSelectionBounds(QFixed *selectionX, QFixed *selectionWidth) const
|
|
||||||
{
|
|
||||||
*selectionX = *selectionWidth = 0;
|
|
||||||
|
|
||||||
if (!selection)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
|
||||||
if (si->position >= selection->start + selection->length
|
|
||||||
|| si->position + itemLength <= selection->start)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
*selectionX = x;
|
|
||||||
*selectionWidth = itemWidth;
|
|
||||||
} else {
|
|
||||||
unsigned short *logClusters = eng->logClusters(si);
|
|
||||||
QGlyphLayout glyphs = eng->shapedGlyphs(si);
|
|
||||||
|
|
||||||
int from = qMax(itemStart, selection->start) - si->position;
|
|
||||||
int to = qMin(itemEnd, selection->start + selection->length) - si->position;
|
|
||||||
if (from >= to)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
int start_glyph = logClusters[from];
|
|
||||||
int end_glyph = (to == eng->length(item)) ? si->num_glyphs : logClusters[to];
|
|
||||||
QFixed soff;
|
|
||||||
QFixed swidth;
|
|
||||||
if (si->analysis.bidiLevel %2) {
|
|
||||||
for (int g = glyphsEnd - 1; g >= end_glyph; --g)
|
|
||||||
soff += glyphs.effectiveAdvance(g);
|
|
||||||
for (int g = end_glyph - 1; g >= start_glyph; --g)
|
|
||||||
swidth += glyphs.effectiveAdvance(g);
|
|
||||||
} else {
|
|
||||||
for (int g = glyphsStart; g < start_glyph; ++g)
|
|
||||||
soff += glyphs.effectiveAdvance(g);
|
|
||||||
for (int g = start_glyph; g < end_glyph; ++g)
|
|
||||||
swidth += glyphs.effectiveAdvance(g);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the starting character is in the middle of a ligature,
|
|
||||||
// selection should only contain the right part of that ligature
|
|
||||||
// glyph, so we need to get the width of the left part here and
|
|
||||||
// add it to *selectionX
|
|
||||||
QFixed leftOffsetInLigature = offsetInLigature(logClusters, glyphs, from,
|
|
||||||
to, start_glyph);
|
|
||||||
*selectionX = x + soff + leftOffsetInLigature;
|
|
||||||
*selectionWidth = swidth - leftOffsetInLigature;
|
|
||||||
// If the ending character is also part of a ligature, swidth does
|
|
||||||
// not contain that part yet, we also need to find out the width of
|
|
||||||
// that left part
|
|
||||||
*selectionWidth += offsetInLigature(logClusters, glyphs, to,
|
|
||||||
eng->length(item), end_glyph);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
|
static void addSelectedRegionsToPath(QTextEngine *eng, int lineNumber, const QPointF &pos, QTextLayout::FormatRange *selection,
|
||||||
QPainterPath *region, QRectF boundingRect)
|
QPainterPath *region, QRectF boundingRect)
|
||||||
{
|
{
|
||||||
@ -1382,18 +1214,9 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
|
|||||||
QFixed pos_y = QFixed::fromReal(position.y());
|
QFixed pos_y = QFixed::fromReal(position.y());
|
||||||
|
|
||||||
cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
|
cursorPosition = qBound(0, cursorPosition, d->layoutData->string.length());
|
||||||
int line = 0;
|
int line = d->lineNumberForTextPosition(cursorPosition);
|
||||||
if (cursorPosition == d->layoutData->string.length()) {
|
if (line < 0)
|
||||||
line = d->lines.size() - 1;
|
line = 0;
|
||||||
} else {
|
|
||||||
// ### binary search
|
|
||||||
for (line = 0; line < d->lines.size(); line++) {
|
|
||||||
const QScriptLine &sl = d->lines[line];
|
|
||||||
if (sl.from <= cursorPosition && sl.from + (int)sl.length > cursorPosition)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line >= d->lines.size())
|
if (line >= d->lines.size())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -1402,7 +1225,15 @@ void QTextLayout::drawCursor(QPainter *p, const QPointF &pos, int cursorPosition
|
|||||||
|
|
||||||
qreal x = position.x() + l.cursorToX(cursorPosition);
|
qreal x = position.x() + l.cursorToX(cursorPosition);
|
||||||
|
|
||||||
int itm = d->findItem(cursorPosition - 1);
|
int itm;
|
||||||
|
|
||||||
|
if (d->visualCursorMovement()) {
|
||||||
|
if (cursorPosition == sl.from + sl.length)
|
||||||
|
cursorPosition--;
|
||||||
|
itm = d->findItem(cursorPosition);
|
||||||
|
} else
|
||||||
|
itm = d->findItem(cursorPosition - 1);
|
||||||
|
|
||||||
QFixed base = sl.base();
|
QFixed base = sl.base();
|
||||||
QFixed descent = sl.descent;
|
QFixed descent = sl.descent;
|
||||||
bool rightToLeft = d->isRightToLeft();
|
bool rightToLeft = d->isRightToLeft();
|
||||||
@ -1512,7 +1343,7 @@ QRectF QTextLine::rect() const
|
|||||||
QRectF QTextLine::naturalTextRect() const
|
QRectF QTextLine::naturalTextRect() const
|
||||||
{
|
{
|
||||||
const QScriptLine& sl = eng->lines[i];
|
const QScriptLine& sl = eng->lines[i];
|
||||||
QFixed x = sl.x + alignLine(eng, sl);
|
QFixed x = sl.x + eng->alignLine(sl);
|
||||||
|
|
||||||
QFixed width = sl.textWidth;
|
QFixed width = sl.textWidth;
|
||||||
if (sl.justified)
|
if (sl.justified)
|
||||||
@ -2635,9 +2466,10 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
|
|||||||
eng->itemize();
|
eng->itemize();
|
||||||
|
|
||||||
const QScriptLine &line = eng->lines[i];
|
const QScriptLine &line = eng->lines[i];
|
||||||
|
bool lastLine = i >= eng->lines.size() - 1;
|
||||||
|
|
||||||
QFixed x = line.x;
|
QFixed x = line.x;
|
||||||
x += alignLine(eng, line);
|
x += eng->alignLine(line);
|
||||||
|
|
||||||
if (!i && !eng->layoutData->items.size()) {
|
if (!i && !eng->layoutData->items.size()) {
|
||||||
*cursorPos = 0;
|
*cursorPos = 0;
|
||||||
@ -2723,21 +2555,29 @@ qreal QTextLine::cursorToX(int *cursorPos, Edge edge) const
|
|||||||
logClusters = eng->logClusters(si);
|
logClusters = eng->logClusters(si);
|
||||||
glyphs = eng->shapedGlyphs(si);
|
glyphs = eng->shapedGlyphs(si);
|
||||||
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
if (si->analysis.flags >= QScriptAnalysis::TabOrObject) {
|
||||||
if(pos == l)
|
if (pos == (reverse ? 0 : l))
|
||||||
x += si->width;
|
x += si->width;
|
||||||
} else {
|
} else {
|
||||||
|
bool rtl = eng->isRightToLeft();
|
||||||
|
bool visual = eng->visualCursorMovement();
|
||||||
if (reverse) {
|
if (reverse) {
|
||||||
int end = qMin(lineEnd, si->position + l) - si->position;
|
int end = qMin(lineEnd, si->position + l) - si->position;
|
||||||
int glyph_end = end == l ? si->num_glyphs : logClusters[end];
|
int glyph_end = end == l ? si->num_glyphs : logClusters[end];
|
||||||
for (int i = glyph_end - 1; i >= glyph_pos; i--)
|
int glyph_start = glyph_pos;
|
||||||
|
if (visual && !rtl && !(lastLine && itm == (visualOrder[nItems - 1] + firstItem)))
|
||||||
|
glyph_start++;
|
||||||
|
for (int i = glyph_end - 1; i >= glyph_start; i--)
|
||||||
x += glyphs.effectiveAdvance(i);
|
x += glyphs.effectiveAdvance(i);
|
||||||
} else {
|
} else {
|
||||||
int start = qMax(line.from - si->position, 0);
|
int start = qMax(line.from - si->position, 0);
|
||||||
int glyph_start = logClusters[start];
|
int glyph_start = logClusters[start];
|
||||||
for (int i = glyph_start; i < glyph_pos; i++)
|
int glyph_end = glyph_pos;
|
||||||
|
if (!visual || !rtl || (lastLine && itm == visualOrder[0] + firstItem))
|
||||||
|
glyph_end--;
|
||||||
|
for (int i = glyph_start; i <= glyph_end; i++)
|
||||||
x += glyphs.effectiveAdvance(i);
|
x += glyphs.effectiveAdvance(i);
|
||||||
}
|
}
|
||||||
x += offsetInLigature(logClusters, glyphs, pos, line.length, glyph_pos);
|
x += eng->offsetInLigature(si, pos, line.length, glyph_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
*cursorPos = pos + si->position;
|
*cursorPos = pos + si->position;
|
||||||
@ -2756,6 +2596,8 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
{
|
{
|
||||||
QFixed x = QFixed::fromReal(_x);
|
QFixed x = QFixed::fromReal(_x);
|
||||||
const QScriptLine &line = eng->lines[i];
|
const QScriptLine &line = eng->lines[i];
|
||||||
|
bool lastLine = i >= eng->lines.size() - 1;
|
||||||
|
int lineNum = i;
|
||||||
|
|
||||||
if (!eng->layoutData)
|
if (!eng->layoutData)
|
||||||
eng->itemize();
|
eng->itemize();
|
||||||
@ -2773,7 +2615,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
x -= line.x;
|
x -= line.x;
|
||||||
x -= alignLine(eng, line);
|
x -= eng->alignLine(line);
|
||||||
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
|
// qDebug("xToCursor: x=%f, cpos=%d", x.toReal(), cpos);
|
||||||
|
|
||||||
QVarLengthArray<int> visualOrder(nItems);
|
QVarLengthArray<int> visualOrder(nItems);
|
||||||
@ -2782,6 +2624,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
|
levels[i] = eng->layoutData->items[i+firstItem].analysis.bidiLevel;
|
||||||
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
|
QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
|
||||||
|
|
||||||
|
bool visual = eng->visualCursorMovement();
|
||||||
if (x <= 0) {
|
if (x <= 0) {
|
||||||
// left of first item
|
// left of first item
|
||||||
int item = visualOrder[0]+firstItem;
|
int item = visualOrder[0]+firstItem;
|
||||||
@ -2798,8 +2641,13 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
|| (line.justified && x < line.width)) {
|
|| (line.justified && x < line.width)) {
|
||||||
// has to be in one of the runs
|
// has to be in one of the runs
|
||||||
QFixed pos;
|
QFixed pos;
|
||||||
|
bool rtl = eng->isRightToLeft();
|
||||||
|
|
||||||
eng->shapeLine(line);
|
eng->shapeLine(line);
|
||||||
|
QVector<int> insertionPoints;
|
||||||
|
if (visual && rtl)
|
||||||
|
eng->insertionPointsForLine(lineNum, insertionPoints);
|
||||||
|
int nchars = 0;
|
||||||
for (int i = 0; i < nItems; ++i) {
|
for (int i = 0; i < nItems; ++i) {
|
||||||
int item = visualOrder[i]+firstItem;
|
int item = visualOrder[i]+firstItem;
|
||||||
QScriptItem &si = eng->layoutData->items[item];
|
QScriptItem &si = eng->layoutData->items[item];
|
||||||
@ -2829,6 +2677,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
|
|
||||||
if (pos + item_width < x) {
|
if (pos + item_width < x) {
|
||||||
pos += item_width;
|
pos += item_width;
|
||||||
|
nchars += end;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// qDebug(" inside run");
|
// qDebug(" inside run");
|
||||||
@ -2873,6 +2722,7 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
} else {
|
} else {
|
||||||
QFixed dist = INT_MAX/256;
|
QFixed dist = INT_MAX/256;
|
||||||
if (si.analysis.bidiLevel % 2) {
|
if (si.analysis.bidiLevel % 2) {
|
||||||
|
if (!visual || rtl || (lastLine && i == nItems - 1)) {
|
||||||
pos += item_width;
|
pos += item_width;
|
||||||
while (gs <= ge) {
|
while (gs <= ge) {
|
||||||
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
|
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
|
||||||
@ -2883,6 +2733,17 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
++gs;
|
++gs;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
while (ge >= gs) {
|
||||||
|
if (glyphs.attributes[ge].clusterStart && qAbs(x-pos) < dist) {
|
||||||
|
glyph_pos = ge;
|
||||||
|
dist = qAbs(x-pos);
|
||||||
|
}
|
||||||
|
pos += glyphs.effectiveAdvance(ge);
|
||||||
|
--ge;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!visual || !rtl || (lastLine && i == 0)) {
|
||||||
while (gs <= ge) {
|
while (gs <= ge) {
|
||||||
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
|
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
|
||||||
glyph_pos = gs;
|
glyph_pos = gs;
|
||||||
@ -2891,10 +2752,31 @@ int QTextLine::xToCursor(qreal _x, CursorPosition cpos) const
|
|||||||
pos += glyphs.effectiveAdvance(gs);
|
pos += glyphs.effectiveAdvance(gs);
|
||||||
++gs;
|
++gs;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
QFixed oldPos = pos;
|
||||||
|
while (gs <= ge) {
|
||||||
|
pos += glyphs.effectiveAdvance(gs);
|
||||||
|
if (glyphs.attributes[gs].clusterStart && qAbs(x-pos) < dist) {
|
||||||
|
glyph_pos = gs;
|
||||||
|
dist = qAbs(x-pos);
|
||||||
|
}
|
||||||
|
++gs;
|
||||||
|
}
|
||||||
|
pos = oldPos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (qAbs(x-pos) < dist) {
|
||||||
|
if (visual) {
|
||||||
|
if (!rtl && i < nItems - 1) {
|
||||||
|
nchars += end;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (rtl && nchars > 0)
|
||||||
|
return insertionPoints[lastLine ? nchars : nchars - 1];
|
||||||
}
|
}
|
||||||
if (qAbs(x-pos) < dist)
|
|
||||||
return si.position + end;
|
return si.position + end;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
Q_ASSERT(glyph_pos != -1);
|
Q_ASSERT(glyph_pos != -1);
|
||||||
int j;
|
int j;
|
||||||
for (j = 0; j < eng->length(item); ++j)
|
for (j = 0; j < eng->length(item); ++j)
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
#include <QtGui/qevent.h>
|
#include <QtGui/qevent.h>
|
||||||
#include <QtGui/qtextformat.h>
|
#include <QtGui/qtextformat.h>
|
||||||
#include <QtGui/qglyphs.h>
|
#include <QtGui/qglyphs.h>
|
||||||
|
#include <QtGui/qtextcursor.h>
|
||||||
|
|
||||||
QT_BEGIN_HEADER
|
QT_BEGIN_HEADER
|
||||||
|
|
||||||
@ -136,6 +137,9 @@ public:
|
|||||||
void setCacheEnabled(bool enable);
|
void setCacheEnabled(bool enable);
|
||||||
bool cacheEnabled() const;
|
bool cacheEnabled() const;
|
||||||
|
|
||||||
|
void setCursorMoveStyle(QTextCursor::MoveStyle style);
|
||||||
|
QTextCursor::MoveStyle cursorMoveStyle() const;
|
||||||
|
|
||||||
void beginLayout();
|
void beginLayout();
|
||||||
void endLayout();
|
void endLayout();
|
||||||
void clearLayout();
|
void clearLayout();
|
||||||
@ -153,6 +157,8 @@ public:
|
|||||||
bool isValidCursorPosition(int pos) const;
|
bool isValidCursorPosition(int pos) const;
|
||||||
int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
|
int nextCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
|
||||||
int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
|
int previousCursorPosition(int oldPos, CursorMode mode = SkipCharacters) const;
|
||||||
|
int leftCursorPosition(int oldPos) const;
|
||||||
|
int rightCursorPosition(int oldPos) const;
|
||||||
|
|
||||||
void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(),
|
void draw(QPainter *p, const QPointF &pos, const QVector<FormatRange> &selections = QVector<FormatRange>(),
|
||||||
const QRectF &clip = QRectF()) const;
|
const QRectF &clip = QRectF()) const;
|
||||||
|
@ -1585,6 +1585,7 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool unknown = false;
|
bool unknown = false;
|
||||||
|
bool visual = cursorMoveStyle() == QTextCursor::Visual;
|
||||||
|
|
||||||
if (false) {
|
if (false) {
|
||||||
}
|
}
|
||||||
@ -1649,11 +1650,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
|
|||||||
#endif
|
#endif
|
||||||
moveCursor(selectionEnd(), false);
|
moveCursor(selectionEnd(), false);
|
||||||
} else {
|
} else {
|
||||||
cursorForward(0, layoutDirection() == Qt::LeftToRight ? 1 : -1);
|
cursorForward(0, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event == QKeySequence::SelectNextChar) {
|
else if (event == QKeySequence::SelectNextChar) {
|
||||||
cursorForward(1, layoutDirection() == Qt::LeftToRight ? 1 : -1);
|
cursorForward(1, visual ? 1 : (layoutDirection() == Qt::LeftToRight ? 1 : -1));
|
||||||
}
|
}
|
||||||
else if (event == QKeySequence::MoveToPreviousChar) {
|
else if (event == QKeySequence::MoveToPreviousChar) {
|
||||||
#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
|
#if !defined(Q_WS_WIN) || defined(QT_NO_COMPLETER)
|
||||||
@ -1664,11 +1665,11 @@ void QLineControl::processKeyEvent(QKeyEvent* event)
|
|||||||
#endif
|
#endif
|
||||||
moveCursor(selectionStart(), false);
|
moveCursor(selectionStart(), false);
|
||||||
} else {
|
} else {
|
||||||
cursorForward(0, layoutDirection() == Qt::LeftToRight ? -1 : 1);
|
cursorForward(0, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (event == QKeySequence::SelectPreviousChar) {
|
else if (event == QKeySequence::SelectPreviousChar) {
|
||||||
cursorForward(1, layoutDirection() == Qt::LeftToRight ? -1 : 1);
|
cursorForward(1, visual ? -1 : (layoutDirection() == Qt::LeftToRight ? -1 : 1));
|
||||||
}
|
}
|
||||||
else if (event == QKeySequence::MoveToNextWord) {
|
else if (event == QKeySequence::MoveToNextWord) {
|
||||||
if (echoMode() == QLineEdit::Normal)
|
if (echoMode() == QLineEdit::Normal)
|
||||||
|
@ -160,6 +160,8 @@ public:
|
|||||||
int cursorWidth() const { return m_cursorWidth; }
|
int cursorWidth() const { return m_cursorWidth; }
|
||||||
void setCursorWidth(int value) { m_cursorWidth = value; }
|
void setCursorWidth(int value) { m_cursorWidth = value; }
|
||||||
|
|
||||||
|
QTextCursor::MoveStyle cursorMoveStyle() const { return m_textLayout.cursorMoveStyle(); }
|
||||||
|
void setCursorMoveStyle(QTextCursor::MoveStyle style) { m_textLayout.setCursorMoveStyle(style); }
|
||||||
|
|
||||||
void moveCursor(int pos, bool mark = false);
|
void moveCursor(int pos, bool mark = false);
|
||||||
void cursorForward(bool mark, int steps)
|
void cursorForward(bool mark, int steps)
|
||||||
@ -167,10 +169,12 @@ public:
|
|||||||
int c = m_cursor;
|
int c = m_cursor;
|
||||||
if (steps > 0) {
|
if (steps > 0) {
|
||||||
while (steps--)
|
while (steps--)
|
||||||
c = m_textLayout.nextCursorPosition(c);
|
c = cursorMoveStyle() == QTextCursor::Visual ? m_textLayout.rightCursorPosition(c)
|
||||||
|
: m_textLayout.nextCursorPosition(c);
|
||||||
} else if (steps < 0) {
|
} else if (steps < 0) {
|
||||||
while (steps++)
|
while (steps++)
|
||||||
c = m_textLayout.previousCursorPosition(c);
|
c = cursorMoveStyle() == QTextCursor::Visual ? m_textLayout.leftCursorPosition(c)
|
||||||
|
: m_textLayout.previousCursorPosition(c);
|
||||||
}
|
}
|
||||||
moveCursor(c, mark);
|
moveCursor(c, mark);
|
||||||
}
|
}
|
||||||
|
@ -1111,6 +1111,34 @@ void QLineEdit::setDragEnabled(bool b)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\property QLineEdit::cursorMoveStyle
|
||||||
|
\brief the movement style of cursor in this line edit
|
||||||
|
\since 4.8
|
||||||
|
|
||||||
|
When this property is set to QTextCursor::Visual, the line edit will use visual
|
||||||
|
movement style. Pressing the left arrow key will always cause the cursor to move
|
||||||
|
left, regardless of the text's writing direction. The same behavior applies to
|
||||||
|
right arrow key.
|
||||||
|
|
||||||
|
When the property is QTextCursor::Logical (the default), within a LTR text block,
|
||||||
|
increase cursor position when pressing left arrow key, decrease cursor position
|
||||||
|
when pressing the right arrow key. If the text block is right to left, the opposite
|
||||||
|
behavior applies.
|
||||||
|
*/
|
||||||
|
|
||||||
|
QTextCursor::MoveStyle QLineEdit::cursorMoveStyle() const
|
||||||
|
{
|
||||||
|
Q_D(const QLineEdit);
|
||||||
|
return d->control->cursorMoveStyle();
|
||||||
|
}
|
||||||
|
|
||||||
|
void QLineEdit::setCursorMoveStyle(QTextCursor::MoveStyle style)
|
||||||
|
{
|
||||||
|
Q_D(QLineEdit);
|
||||||
|
d->control->setCursorMoveStyle(style);
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\property QLineEdit::acceptableInput
|
\property QLineEdit::acceptableInput
|
||||||
\brief whether the input satisfies the inputMask and the
|
\brief whether the input satisfies the inputMask and the
|
||||||
|
@ -43,6 +43,7 @@
|
|||||||
#define QLINEEDIT_H
|
#define QLINEEDIT_H
|
||||||
|
|
||||||
#include <QtGui/qframe.h>
|
#include <QtGui/qframe.h>
|
||||||
|
#include <QtGui/qtextcursor.h>
|
||||||
#include <QtCore/qstring.h>
|
#include <QtCore/qstring.h>
|
||||||
#include <QtCore/qmargins.h>
|
#include <QtCore/qmargins.h>
|
||||||
|
|
||||||
@ -158,6 +159,9 @@ public:
|
|||||||
void setDragEnabled(bool b);
|
void setDragEnabled(bool b);
|
||||||
bool dragEnabled() const;
|
bool dragEnabled() const;
|
||||||
|
|
||||||
|
void setCursorMoveStyle(QTextCursor::MoveStyle style);
|
||||||
|
QTextCursor::MoveStyle cursorMoveStyle() const;
|
||||||
|
|
||||||
QString inputMask() const;
|
QString inputMask() const;
|
||||||
void setInputMask(const QString &inputMask);
|
void setInputMask(const QString &inputMask);
|
||||||
bool hasAcceptableInput() const;
|
bool hasAcceptableInput() const;
|
||||||
|
@ -71,6 +71,10 @@ private slots:
|
|||||||
void bidiReorderString();
|
void bidiReorderString();
|
||||||
void bidiCursor_qtbug2795();
|
void bidiCursor_qtbug2795();
|
||||||
void bidiCursor_PDF();
|
void bidiCursor_PDF();
|
||||||
|
void bidiCursorMovement_data();
|
||||||
|
void bidiCursorMovement();
|
||||||
|
void bidiCursorLogicalMovement_data();
|
||||||
|
void bidiCursorLogicalMovement();
|
||||||
};
|
};
|
||||||
|
|
||||||
tst_QComplexText::tst_QComplexText()
|
tst_QComplexText::tst_QComplexText()
|
||||||
@ -185,6 +189,89 @@ void tst_QComplexText::bidiCursor_qtbug2795()
|
|||||||
QVERIFY(x1 == x2);
|
QVERIFY(x1 == x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QComplexText::bidiCursorMovement_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("logical");
|
||||||
|
QTest::addColumn<int>("basicDir");
|
||||||
|
|
||||||
|
const LV *data = logical_visual;
|
||||||
|
while ( data->name ) {
|
||||||
|
//next we fill it with data
|
||||||
|
QTest::newRow( data->name )
|
||||||
|
<< QString::fromUtf8( data->logical )
|
||||||
|
<< (int) data->basicDir;
|
||||||
|
data++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QComplexText::bidiCursorMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
|
||||||
|
QTextLayout layout(logical);
|
||||||
|
|
||||||
|
QTextOption option = layout.textOption();
|
||||||
|
option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
|
||||||
|
layout.setTextOption(option);
|
||||||
|
bool moved;
|
||||||
|
int oldPos, newPos = 0;
|
||||||
|
qreal x, newX;
|
||||||
|
|
||||||
|
layout.beginLayout();
|
||||||
|
QTextLine line = layout.createLine();
|
||||||
|
layout.endLayout();
|
||||||
|
|
||||||
|
newX = line.cursorToX(0);
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
x = newX;
|
||||||
|
newX = line.cursorToX(oldPos);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
QVERIFY(newX >= x);
|
||||||
|
newPos = layout.rightCursorPosition(oldPos);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
QVERIFY(newX <= x);
|
||||||
|
newPos = layout.leftCursorPosition(oldPos);
|
||||||
|
}
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QComplexText::bidiCursorLogicalMovement_data()
|
||||||
|
{
|
||||||
|
bidiCursorMovement_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QComplexText::bidiCursorLogicalMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
|
||||||
|
QTextLayout layout(logical);
|
||||||
|
|
||||||
|
QTextOption option = layout.textOption();
|
||||||
|
option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
|
||||||
|
layout.setTextOption(option);
|
||||||
|
bool moved;
|
||||||
|
int oldPos, newPos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
newPos = layout.nextCursorPosition(oldPos);
|
||||||
|
QVERIFY(newPos >= oldPos);
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved);
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
newPos = layout.previousCursorPosition(oldPos);
|
||||||
|
QVERIFY(newPos <= oldPos);
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved);
|
||||||
|
}
|
||||||
|
|
||||||
void tst_QComplexText::bidiCursor_PDF()
|
void tst_QComplexText::bidiCursor_PDF()
|
||||||
{
|
{
|
||||||
QString str = QString::fromUtf8("\342\200\252hello\342\200\254");
|
QString str = QString::fromUtf8("\342\200\252hello\342\200\254");
|
||||||
|
@ -282,6 +282,12 @@ private slots:
|
|||||||
void validateAndSet();
|
void validateAndSet();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void bidiVisualMovement_data();
|
||||||
|
void bidiVisualMovement();
|
||||||
|
|
||||||
|
void bidiLogicalMovement_data();
|
||||||
|
void bidiLogicalMovement();
|
||||||
|
|
||||||
protected slots:
|
protected slots:
|
||||||
#ifdef QT3_SUPPORT
|
#ifdef QT3_SUPPORT
|
||||||
void lostFocus();
|
void lostFocus();
|
||||||
@ -3760,5 +3766,135 @@ void tst_QLineEdit::QTBUG13520_textNotVisible()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void tst_QLineEdit::bidiVisualMovement_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("logical");
|
||||||
|
QTest::addColumn<int>("basicDir");
|
||||||
|
QTest::addColumn<IntList>("positionList");
|
||||||
|
|
||||||
|
QTest::newRow("Latin text")
|
||||||
|
<< QString::fromUtf8("abc")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (IntList() << 0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("Hebrew text, one item")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("Hebrew text after Latin text")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
|
||||||
|
QTest::newRow("Latin text after Hebrew text")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
|
||||||
|
QTest::newRow("LTR, 3 items")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
|
||||||
|
QTest::newRow("RTL, 3 items")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
|
||||||
|
QTest::newRow("LTR, 4 items")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
|
||||||
|
QTest::newRow("RTL, 4 items")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QLineEdit::bidiVisualMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
QFETCH(IntList, positionList);
|
||||||
|
|
||||||
|
QLineEdit le;
|
||||||
|
le.setText(logical);
|
||||||
|
|
||||||
|
le.setCursorMoveStyle(QTextCursor::Visual);
|
||||||
|
le.setCursorPosition(0);
|
||||||
|
|
||||||
|
bool moved;
|
||||||
|
int i = 0, oldPos, newPos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == positionList[i]);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
QTest::keyClick(&le, Qt::Key_Right);
|
||||||
|
} else
|
||||||
|
QTest::keyClick(&le, Qt::Key_Left);
|
||||||
|
newPos = le.cursorPosition();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
i++;
|
||||||
|
} while (moved);
|
||||||
|
|
||||||
|
QVERIFY(i == positionList.size());
|
||||||
|
|
||||||
|
do {
|
||||||
|
i--;
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == positionList[i]);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
QTest::keyClick(&le, Qt::Key_Left);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
QTest::keyClick(&le, Qt::Key_Right);
|
||||||
|
}
|
||||||
|
newPos = le.cursorPosition();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved && i >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QLineEdit::bidiLogicalMovement_data()
|
||||||
|
{
|
||||||
|
bidiVisualMovement_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QLineEdit::bidiLogicalMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
|
||||||
|
QLineEdit le;
|
||||||
|
le.setText(logical);
|
||||||
|
|
||||||
|
le.setCursorMoveStyle(QTextCursor::Logical);
|
||||||
|
le.setCursorPosition(0);
|
||||||
|
|
||||||
|
bool moved;
|
||||||
|
int i = 0, oldPos, newPos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == i);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
QTest::keyClick(&le, Qt::Key_Right);
|
||||||
|
} else
|
||||||
|
QTest::keyClick(&le, Qt::Key_Left);
|
||||||
|
newPos = le.cursorPosition();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
i++;
|
||||||
|
} while (moved);
|
||||||
|
|
||||||
|
do {
|
||||||
|
i--;
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == i);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
QTest::keyClick(&le, Qt::Key_Left);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
QTest::keyClick(&le, Qt::Key_Right);
|
||||||
|
}
|
||||||
|
newPos = le.cursorPosition();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved && i >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QLineEdit)
|
QTEST_MAIN(tst_QLineEdit)
|
||||||
#include "tst_qlineedit.moc"
|
#include "tst_qlineedit.moc"
|
||||||
|
@ -42,7 +42,6 @@
|
|||||||
|
|
||||||
#include <QtTest/QtTest>
|
#include <QtTest/QtTest>
|
||||||
|
|
||||||
|
|
||||||
#include <qtextedit.h>
|
#include <qtextedit.h>
|
||||||
#include <qtextcursor.h>
|
#include <qtextcursor.h>
|
||||||
#include <qtextlist.h>
|
#include <qtextlist.h>
|
||||||
@ -69,6 +68,7 @@ typedef QList<keyPairType> pairListType;
|
|||||||
Q_DECLARE_METATYPE(pairListType);
|
Q_DECLARE_METATYPE(pairListType);
|
||||||
Q_DECLARE_METATYPE(keyPairType);
|
Q_DECLARE_METATYPE(keyPairType);
|
||||||
Q_DECLARE_METATYPE(QList<bool>);
|
Q_DECLARE_METATYPE(QList<bool>);
|
||||||
|
Q_DECLARE_METATYPE(QList<int>);
|
||||||
|
|
||||||
#ifdef Q_WS_MAC
|
#ifdef Q_WS_MAC
|
||||||
#include <Carbon/Carbon.h>
|
#include <Carbon/Carbon.h>
|
||||||
@ -205,6 +205,11 @@ private slots:
|
|||||||
#ifndef QT_NO_CONTEXTMENU
|
#ifndef QT_NO_CONTEXTMENU
|
||||||
void taskQTBUG_7902_contextMenuCrash();
|
void taskQTBUG_7902_contextMenuCrash();
|
||||||
#endif
|
#endif
|
||||||
|
void bidiVisualMovement_data();
|
||||||
|
void bidiVisualMovement();
|
||||||
|
|
||||||
|
void bidiLogicalMovement_data();
|
||||||
|
void bidiLogicalMovement();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void createSelection();
|
void createSelection();
|
||||||
@ -2235,5 +2240,147 @@ void tst_QTextEdit::taskQTBUG_7902_contextMenuCrash()
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void tst_QTextEdit::bidiVisualMovement_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("logical");
|
||||||
|
QTest::addColumn<int>("basicDir");
|
||||||
|
QTest::addColumn<QList<int> >("positionList");
|
||||||
|
|
||||||
|
QTest::newRow("Latin text")
|
||||||
|
<< QString::fromUtf8("abc")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("Hebrew text, one item")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 3);
|
||||||
|
QTest::newRow("Hebrew text after Latin text")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
|
||||||
|
QTest::newRow("Latin text after Hebrew text")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 6 << 5 << 4 << 3);
|
||||||
|
QTest::newRow("LTR, 3 items")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
|
||||||
|
QTest::newRow("RTL, 3 items")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 9);
|
||||||
|
QTest::newRow("LTR, 4 items")
|
||||||
|
<< QString::fromUtf8("abc\327\220\327\221\327\222abc\327\220\327\221\327\222")
|
||||||
|
<< (int) QChar::DirL
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
|
||||||
|
QTest::newRow("RTL, 4 items")
|
||||||
|
<< QString::fromUtf8("\327\220\327\221\327\222abc\327\220\327\221\327\222abc")
|
||||||
|
<< (int) QChar::DirR
|
||||||
|
<< (QList<int>() << 0 << 1 << 2 << 5 << 4 << 3 << 6 << 7 << 8 << 12 << 11 << 10 << 9);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QTextEdit::bidiVisualMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
QFETCH(QList<int>, positionList);
|
||||||
|
|
||||||
|
ed->setText(logical);
|
||||||
|
|
||||||
|
QTextOption option = ed->document()->defaultTextOption();
|
||||||
|
option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
|
||||||
|
ed->document()->setDefaultTextOption(option);
|
||||||
|
|
||||||
|
ed->document()->setDefaultCursorMoveStyle(QTextCursor::Visual);
|
||||||
|
ed->moveCursor(QTextCursor::Start);
|
||||||
|
ed->show();
|
||||||
|
|
||||||
|
bool moved;
|
||||||
|
int i = 0, oldPos, newPos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == positionList[i]);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
ed->moveCursor(QTextCursor::Right);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ed->moveCursor(QTextCursor::Left);
|
||||||
|
}
|
||||||
|
newPos = ed->textCursor().position();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
i++;
|
||||||
|
} while (moved);
|
||||||
|
|
||||||
|
QVERIFY(i == positionList.size());
|
||||||
|
|
||||||
|
do {
|
||||||
|
i--;
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == positionList[i]);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
ed->moveCursor(QTextCursor::Left);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ed->moveCursor(QTextCursor::Right);
|
||||||
|
}
|
||||||
|
newPos = ed->textCursor().position();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved && i >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QTextEdit::bidiLogicalMovement_data()
|
||||||
|
{
|
||||||
|
bidiVisualMovement_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QTextEdit::bidiLogicalMovement()
|
||||||
|
{
|
||||||
|
QFETCH(QString, logical);
|
||||||
|
QFETCH(int, basicDir);
|
||||||
|
|
||||||
|
ed->setText(logical);
|
||||||
|
|
||||||
|
QTextOption option = ed->document()->defaultTextOption();
|
||||||
|
option.setTextDirection(basicDir == QChar::DirL ? Qt::LeftToRight : Qt::RightToLeft);
|
||||||
|
ed->document()->setDefaultTextOption(option);
|
||||||
|
|
||||||
|
ed->document()->setDefaultCursorMoveStyle(QTextCursor::Logical);
|
||||||
|
ed->moveCursor(QTextCursor::Start);
|
||||||
|
ed->show();
|
||||||
|
|
||||||
|
bool moved;
|
||||||
|
int i = 0, oldPos, newPos = 0;
|
||||||
|
|
||||||
|
do {
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == i);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
ed->moveCursor(QTextCursor::Right);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ed->moveCursor(QTextCursor::Left);
|
||||||
|
}
|
||||||
|
newPos = ed->textCursor().position();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
i++;
|
||||||
|
} while (moved);
|
||||||
|
|
||||||
|
do {
|
||||||
|
i--;
|
||||||
|
oldPos = newPos;
|
||||||
|
QVERIFY(oldPos == i);
|
||||||
|
if (basicDir == QChar::DirL) {
|
||||||
|
ed->moveCursor(QTextCursor::Left);
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
ed->moveCursor(QTextCursor::Right);
|
||||||
|
}
|
||||||
|
newPos = ed->textCursor().position();
|
||||||
|
moved = (oldPos != newPos);
|
||||||
|
} while (moved && i >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QTextEdit)
|
QTEST_MAIN(tst_QTextEdit)
|
||||||
#include "tst_qtextedit.moc"
|
#include "tst_qtextedit.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user