QTextLayout: How to make texts selectable by mouse (or arrow keys, if possible)
-
Greetings!
I came across the "Low-Level Text Layouts":http://doc.qt.digia.com/qq/qq24-textlayouts.html and successfully made a simple program based from the examples.
However, I need to make the texts selectable by mouse and/or arrow keys and have text cursor support if possible. Thanks for any help!
-
I was working on the same problem for a while and developed the following solution:
When left mouse click is received I set the flag as selection started and then monitor mouseMoveEvent's. When the button released I can finalize the selection and unset the flag. Then, QTextlayout::draw() function has the third parameter of type QVectorQTextLayout::FormatRange where you can set the ranges you need. If you want to update selection on the fly, it could be potentially slow because you will need to make a new QVector on every mouse movement (when left mouse button is pressed) and the redraw your QTextLayout. Alternatively, you can redraw only on final mouse relese but then the user would not know what the selection is till he/she actually releases the button. Below are my code snippets from actually working prototype.
// header
@class TextView : public QWidget {
...
private:
QTextLayout *textLayout;
QString *textToShow;
QVectorQTextLayout::FormatRange vector;
...
@// implementation
@void TextView::paintEvent(QPaintEvent*)
{
QPainter painter(this);
textLayout->draw(&painter, QPoint(borderX_InPixels, borderY_InPixels), vector);
}void TextView::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
selectionStarted = true;
//get line number
int lineNumber = (event->y() - borderY_InPixels) / lineHeight;
lineNumber = qMin(lineNumber, textLayout->lineCount() - 1);
lineNumber = qMax(0, lineNumber);//get character number QTextLine line = textLayout->lineAt(lineNumber); selectedCharacterFirst = line.xToCursor(event->x() - borderX_InPixels); }
}
void TextView::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
//get line number
int lineNumber = (event->y() - borderY_InPixels) / lineHeight;
lineNumber = qMin(lineNumber, textLayout->lineCount() - 1);
lineNumber = qMax(0, lineNumber);//get character number QTextLine line = textLayout->lineAt(lineNumber); selectedCharacterLast = line.xToCursor(event->x() - borderX_InPixels); updateOnSelection(); selectionStarted = false; selectedCharacterFirst = selectedCharacterLast = 0; timerForScrollingWhileSelecting->stop(); }
}
void TextView::mouseMoveEvent(QMouseEvent *event)
{
if (selectionStarted)
{
//get line number
int lineNumber = (event->y() - borderY_InPixels) / lineHeight;
lineNumber = qMin(lineNumber, textLayout->lineCount() - 1);
lineNumber = qMax(0, lineNumber);//get character number QTextLine line = textLayout->lineAt(lineNumber); selectedCharacterLast = line.xToCursor(event->x() - borderX_InPixels); if ( event->y() + parentsHeight < textLayout->boundingRect().height() + 2*borderY_InPixels ) { directionWhenScrolling = 0; //up if (!timerForScrollingWhileSelecting->isActive()) timerForScrollingWhileSelecting->start(); } else if ( event->y() > parentsHeight + 5 ) { directionWhenScrolling = 1; //down if (!timerForScrollingWhileSelecting->isActive()) timerForScrollingWhileSelecting->start(); } updateOnSelection(); }
}
void TextView::updateOnSelection()
{
if (!(QApplication::queryKeyboardModifiers() & Qt::ControlModifier))
{
vector.clear(); //otherwise selection won't be updated on the fly
}int reallyCharacterFirst = qMin(selectedCharacterFirst, selectedCharacterLast); int reallyCharacterLast = qMax(selectedCharacterFirst, selectedCharacterLast); QTextLayout::FormatRange formatRange; formatRange.start = reallyCharacterFirst; formatRange.length = reallyCharacterLast - reallyCharacterFirst; QTextCharFormat textCharFormat; textCharFormat.setBackground(QBrush(Qt::cyan)); formatRange.format = textCharFormat; vector.push_back(formatRange); update();
}@
Hope this helps,
Yuriy