PDA

View Full Version : Unexpected QTextCursor behaviour



Binary91
9th July 2015, 18:39
Hey,

I'm trying to catch the users input in a QTextEdit and check for illegal characters.

I catch the signal "textChanged()" and check the input (edit->toPlainText()) for illegal characters.

If some illegal characters are found, I replace or remove them and set the new text (edit->setPlainText(QString)).

Now my problem is the cursor position. I found out that calling QTextEdit::setPlainText(QString) sets a new QTextCursor at position '0'.

I manage this by saving the QTextCursor's recent position (before changing input) and set this position to the new QTextCursor, like this:

this->blockSignals(true);
QString input = this->toPlainText();

// ... some checks and replaces with input

QTextCursor cursor = this->textCursor();
int cursorPos = this->textCursor().position();

this->setPlainText(input); // this deletes the previous cursor

cursor.setPosition(cursorPos);
this->setTextCursor(cursor);

this->blockSignals(false);

This works great. What I don't under stand is, why the following code (exactly the same commands, only different order) doesn't work:

this->blockSignals(true);
QString input = this->toPlainText();

// ... some checks and replaces with input

QTextCursor cursor = this->textCursor();
int cursorPos = this->textCursor().position();
cursor.setPosition(cursorPos);

this->setPlainText(input); // this deletes the previous cursor

this->setTextCursor(cursor);

this->blockSignals(false);
I would understand that if "cursor" was a pointer, but it isn't!! How can QTextEdit recognize that I set the new cursor position before setting new text?

I would really appreciate a good explanation of this strange behaviour...

Thank you in anticipation!
Binary

anda_skoa
10th July 2015, 09:05
The object might not be a pointer, but the objects internally could easily have a permanent relationship.

E.g. a QAbstractItemModel knows about the QPersistentModelIndex instances it created and can update them in necessary.

Cheers,
_

Binary91
10th July 2015, 10:02
Hi,

hm that confuses me a bit. How does Qt manage this?

Let's take the example above. I have a QTextEdit that internaly has a QTextCursor member (non-pointer).

In my function, I create a new QTextCursor object that should be a copy of the QTextEdit's QTextCursor:

QTextCursor cursorCopy = edit->textCursor();
Question 1: How does Qt manage this now? How can Qt have a connection between this copy and the internal QTextCursor of QTextEdit if it is just a copy, not a pointer??

Question 2: If you were right and there exists any mystic connection between the copy and the original QTextCursor, then the following should work, too:

edit->textCursor().setPosition(int);
But it doesn't work. I can only set a new cursor position by changing the cursor position in the copy and replacing the original QTextCursor with the copy by:

edit->setTextCursor(QTextCursor);
Well, for me it seems to be logical that it doesn't work, because:

edit->textCursor();
only gives me a copy, so changing cursor postion via:

edit->textCursor().setPosition(int);
will never apply to QTextEdit but only to the copy.

So after all, I'm still wondering about this behaviour. Why does it have some features like a pointer with a real connection to QTextEdit's textcursor when the code above fails (like it is expected for a non-pointer copy) ??

Sorry but I really want to understand that...

anda_skoa
10th July 2015, 10:48
hm that confuses me a bit. How does Qt manage this?

Let's take the example above. I have a QTextEdit that internaly has a QTextCursor member (non-pointer).

In my function, I create a new QTextCursor object that should be a copy of the QTextEdit's QTextCursor:

QTextCursor cursorCopy = edit->textCursor();
Question 1: How does Qt manage this now? How can Qt have a connection between this copy and the internal QTextCursor of QTextEdit if it is just a copy, not a pointer??

The QTextCursor holds a pointer to the QTexDocument that created it. And it registers itself with the document.
So both objects know each other.
If you create a copy, the new copy also registers itself with the document.




Question 2: If you were right and there exists any mystic connection between the copy and the original QTextCursor, then the following should work, too:

edit->textCursor().setPosition(int);
But it doesn't work.

That changes the position of the tempory copy. Since that object is not the document's "current cursor" it doesn't affect the document.



I can only set a new cursor position by changing the cursor position in the copy and replacing the original QTextCursor with the copy by:

edit->setTextCursor(QTextCursor);

That replaces the document's "current cursor" with the one you pass in.



Well, for me it seems to be logical that it doesn't work, because:

edit->textCursor();
only gives me a copy, so changing cursor postion via:

edit->textCursor().setPosition(int);
will never apply to QTextEdit but only to the copy.

Yes.



Sorry but I really want to understand that...

In your first variant, you save the position. Then you implicitly clear the document by setting totally new content.
All cursors get their position reset.
Then you modify one cursor with the saved position.
You then set a cursor that now has position == saved position.

In your second variant, you set the cursor's position. Then you implicitly clear the document by setting totally new content.
All cursors get their positions reset.
You then set a cursor that now has position == 0.

See QTextDocumentPrivate::clear(), qtextdocument_p.cpp


Cheers,
_

Binary91
10th July 2015, 12:48
Hi,

at first, thank you for you explanations!

Well, if I understand you right, QTextDocument is the "mother" or "parent" of QTextCursor and the original QTextCursor has a pointer to it and by copying it to my new QTextCursor I also copy the pointer to QTextDocument, so the "mystic" connection goes through QTextDocument.

That would make sense, if the copy exactly changed its position like the original QTextCursor (what I thought till a view minutes ago).

Let's take the example again:

this->blockSignals(true);

QTextCursor cursorCopy = edit->textCursor();
int intCursorPosPre = edit->textCursor().position();
QMessageBox::information(this, "Before changing QTextDocument", QString::number(edit->textCursor().position()) + "\n" + QString::number(cursorCopy.position()));
this->setPlainText(stringText);
QMessageBox::information(this, "After changing QTextDocument", QString::number(edit->textCursor().position()) + "\n" + QString::number(cursorCopy.position()));
if(intTextLengthPre == intTextLengthPost)
cursorCopy.setPosition(intCursorPosPre);
else
cursorCopy.setPosition(intCursorPosPre-1);
QMessageBox::information(this, "After setting previously saved position to cursorCopy", QString::number(edit->textCursor().position()) + "\n" + QString::number(cursorCopy.position()));
edit->setTextCursor(cursorCopy);
QMessageBox::information(this, "After setting cursorCopy to cursorOriginal", QString::number(edit->textCursor().position()) + "\n" + QString::number(cursorCopy.position()));

this->blockSignals(false);
Well, I found out, that when "stringText" did not change in prior algorithms (hence, QTextEdit is updated with the same text it had before), then the following:

edit->setPlainText(QString);
only changes the cursorOriginal (it sets it to zero). The cursorCopy is not changed, hence its position is always the same and I don't need to save it into an int...

Instead, when "stringText" changes in prior algorithms (e.g. it was shorten from length 10 to length 9 characters), then:

edit->setPlainText(QString);
sets the cursorOriginal to zero again, BUT the cursorCopy increments by 1!

Can you explain this behaviour to me? It seems that it is not that easy that both, the cursorOriginal and the cursorCopy, change identically...

EDIT:
Ah after trying some constellations, I detected the rule in this behaviour:
After setting new text:

edit->setPlainText(QString);
the cursorOriginal always sets to zero and the cursorCopy always sets to the last possible position!! :D:confused:

But I still don't see a reason or a sense behind this behaviour!