FlatTextarea handles tags on insertFromMime and tags editing.

Fixed dependent messages update when message was edited.
Fixed entities parsing in Text when they follow one after another.
This commit is contained in:
John Preston 2016-05-04 19:46:24 +03:00
parent 5300962ef3
commit 45143c40c9
6 changed files with 293 additions and 127 deletions

View File

@ -6960,12 +6960,12 @@ void HistoryMessage::initDimensions() {
if (_media->isDisplayed()) {
if (_text.hasSkipBlock()) {
_text.removeSkipBlock();
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
} else if (!_text.hasSkipBlock()) {
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
}
@ -7103,6 +7103,8 @@ void HistoryMessage::applyEdition(const MTPDmessage &message) {
keyboard->oldTop = keyboardTop;
}
}
App::historyUpdateDependent(this);
}
void HistoryMessage::updateMedia(const MTPMessageMedia *media) {
@ -7211,11 +7213,11 @@ void HistoryMessage::setMedia(const MTPMessageMedia *media) {
initMedia(media, t);
if (_media && _media->isDisplayed() && !mediaWasDisplayed) {
_text.removeSkipBlock();
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
} else if (mediaWasDisplayed && (!_media || !_media->isDisplayed())) {
_text.setSkipBlock(skipBlockWidth(), skipBlockHeight());
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
}
@ -7236,7 +7238,7 @@ void HistoryMessage::setText(const QString &text, const EntitiesInText &entities
break;
}
}
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
@ -7395,7 +7397,7 @@ void HistoryMessage::setViewsCount(int32 count) {
} else {
if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
setPendingInitDimensions();
@ -7410,7 +7412,7 @@ void HistoryMessage::setId(MsgId newId) {
} else {
if (_text.hasSkipBlock()) {
_text.setSkipBlock(HistoryMessage::skipBlockWidth(), HistoryMessage::skipBlockHeight());
_textWidth = 0;
_textWidth = -1;
_textHeight = 0;
}
setPendingInitDimensions();
@ -8079,7 +8081,6 @@ bool HistoryService::updatePinned(bool force) {
updatePinnedText();
}
if (force) {
setPendingInitDimensions();
if (gotDependencyItem && App::wnd()) {
App::wnd()->notifySettingGot();
}
@ -8208,7 +8209,9 @@ void HistoryService::setServiceText(const QString &text) {
textstyleSet(&st::serviceTextStyle);
_text.setText(st::msgServiceFont, text, _historySrvOptions);
textstyleRestore();
initDimensions();
setPendingInitDimensions();
_textWidth = -1;
_textHeight = 0;
}
void HistoryService::draw(Painter &p, const QRect &r, TextSelection selection, uint64 ms) const {

View File

@ -1566,7 +1566,8 @@ protected:
}
Text _text = { int(st::msgMinWidth) };
int32 _textWidth, _textHeight;
int _textWidth = -1;
int _textHeight = 0;
HistoryMediaPtr _media;

View File

@ -83,52 +83,52 @@ inline EmojiPtr emojiFromUrl(const QString &url) {
return emojiFromKey(url.midRef(10).toULongLong(0, 16)); // skip emoji://e.
}
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *e, int *plen = 0) {
EmojiPtr emoji = 0;
if (ch + 1 < e && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
inline EmojiPtr emojiFromText(const QChar *ch, const QChar *end, int *outLength = nullptr) {
EmojiPtr emoji = nullptr;
if (ch + 1 < end && ((ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) || (((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0x20E3))) {
uint32 code = (ch->unicode() << 16) | (ch + 1)->unicode();
emoji = emojiGet(code);
if (emoji) {
if (emoji == TwoSymbolEmoji) { // check two symbol
if (ch + 3 >= e) {
if (ch + 3 >= end) {
emoji = 0;
} else {
uint32 code2 = ((uint32((ch + 2)->unicode()) << 16) | uint32((ch + 3)->unicode()));
emoji = emojiGet(code, code2);
}
} else {
if (ch + 2 < e && (ch + 2)->unicode() == 0x200D) { // check sequence
EmojiPtr seq = emojiGet(ch, e);
if (ch + 2 < end && (ch + 2)->unicode() == 0x200D) { // check sequence
EmojiPtr seq = emojiGet(ch, end);
if (seq) {
emoji = seq;
}
}
}
}
} else if (ch + 2 < e && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
} else if (ch + 2 < end && ((ch->unicode() >= 0x30 && ch->unicode() < 0x3A) || ch->unicode() == 0x23 || ch->unicode() == 0x2A) && (ch + 1)->unicode() == 0xFE0F && (ch + 2)->unicode() == 0x20E3) {
uint32 code = (ch->unicode() << 16) | (ch + 2)->unicode();
emoji = emojiGet(code);
if (plen) *plen = emoji->len + 1;
if (outLength) *outLength = emoji->len + 1;
return emoji;
} else if (ch < e) {
} else if (ch < end) {
emoji = emojiGet(ch->unicode());
t_assert(emoji != TwoSymbolEmoji);
}
if (emoji) {
int32 len = emoji->len + ((ch + emoji->len < e && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < e && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
int32 len = emoji->len + ((ch + emoji->len < end && (ch + emoji->len)->unicode() == 0xFE0F) ? 1 : 0);
if (emoji->color && (ch + len + 1 < end && (ch + len)->isHighSurrogate() && (ch + len + 1)->isLowSurrogate())) { // color
uint32 color = ((uint32((ch + len)->unicode()) << 16) | uint32((ch + len + 1)->unicode()));
EmojiPtr col = emojiGet(emoji, color);
if (col && col != emoji) {
len += col->len - emoji->len;
emoji = col;
if (ch + len < e && (ch + len)->unicode() == 0xFE0F) {
if (ch + len < end && (ch + len)->unicode() == 0xFE0F) {
++len;
}
}
}
if (plen) *plen = len;
if (outLength) *outLength = len;
}
return emoji;

View File

@ -310,7 +310,7 @@ EmojiPtr FlatTextarea::getSingleEmoji() const {
return emojiFromUrl(imageName);
}
}
return 0;
return nullptr;
}
QString FlatTextarea::getInlineBotQuery(UserData **outInlineBot, QString *outInlineBotUsername) const {
@ -464,14 +464,16 @@ void FlatTextarea::insertMentionHashtagOrBotCommand(const QString &data, const Q
break;
}
if (tagId.isEmpty()) {
c.insertText(data + ' ');
QTextCharFormat format = c.charFormat();
format.setAnchor(false);
format.setAnchorName(QString());
format.clearForeground();
c.insertText(data + ' ', format);
} else {
QTextCharFormat defaultFormat = c.charFormat(), linkFormat = defaultFormat;
linkFormat.setAnchor(true);
linkFormat.setAnchorName(tagId + '/' + QString::number(rand_value<uint32>()));
linkFormat.setForeground(st::defaultTextStyle.linkFg);
c.insertText(data, linkFormat);
c.insertText(qsl(" "), defaultFormat);
_insertedTags.clear();
_insertedTags.push_back({ 0, data.size(), tagId + '/' + QString::number(rand_value<uint32>()) });
c.insertText(data + ' ');
_insertedTags.clear();
}
}
@ -792,16 +794,21 @@ QStringList FlatTextarea::linksList() const {
void FlatTextarea::insertFromMimeData(const QMimeData *source) {
auto mime = str_const_toString(TagsMimeType);
auto text = source->text();
if (source->hasFormat(mime)) {
auto tagsData = source->data(mime);
_settingTags = deserializeTagsList(tagsData, source->text().size());
_insertedTags = deserializeTagsList(tagsData, text.size());
} else {
_settingTags.clear();
_insertedTags.clear();
}
_realInsertPosition = textCursor().position();
_realCharsAdded = text.size();
QTextEdit::insertFromMimeData(source);
_settingTags.clear();
if (!_inDrop) emit spacedReturnedPasted();
if (!_inDrop) {
emit spacedReturnedPasted();
_insertedTags.clear();
_realInsertPosition = -1;
}
}
void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
@ -811,8 +818,11 @@ void FlatTextarea::insertEmoji(EmojiPtr emoji, QTextCursor c) {
imageFormat.setHeight(eh / cIntRetinaFactor());
imageFormat.setName(qsl("emoji://e.") + QString::number(emojiKey(emoji), 16));
imageFormat.setVerticalAlignment(QTextCharFormat::AlignBaseline);
imageFormat.setAnchorName(c.charFormat().anchorName());
if (c.charFormat().isAnchor()) {
imageFormat.setAnchor(true);
imageFormat.setAnchorName(c.charFormat().anchorName());
imageFormat.setForeground(st::defaultTextStyle.linkFg);
}
static QString objectReplacement(QChar::ObjectReplacementCharacter);
c.insertText(objectReplacement, imageFormat);
}
@ -833,92 +843,234 @@ void FlatTextarea::checkContentHeight() {
}
}
void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
int32 replacePosition = -1, replaceLen = 0;
const EmojiData *emoji = nullptr;
namespace {
static QString regular = qsl("Open Sans"), semibold = qsl("Open Sans Semibold");
bool checkTilde = !cRetina() && (font().pixelSize() == 13) && (font().family() == regular), wasTildeFragment = false;
// Optimization: with null page size document does not re-layout
// on each insertText / mergeCharFormat.
void prepareFormattingOptimization(QTextDocument *document) {
if (!document->pageSize().isNull()) {
document->setPageSize(QSizeF(0, 0));
}
}
QTextDocument *doc(document());
void removeTags(QTextDocument *document, int from, int end) {
QTextCursor c(document->docHandle(), from);
c.setPosition(end, QTextCursor::KeepAnchor);
QTextCharFormat format;
format.setAnchor(false);
format.setAnchorName(QString());
format.setForeground(st::black);
c.mergeCharFormat(format);
}
// Returns the position of the first inserted tag or "changedEnd" value if none found.
int processInsertedTags(QTextDocument *document, int changedPosition, int changedEnd, const FlatTextarea::TagList &tags) {
int firstTagStart = changedEnd;
int applyNoTagFrom = changedEnd;
for_const (auto &tag, tags) {
int tagFrom = changedPosition + tag.offset;
int tagTo = tagFrom + tag.length;
accumulate_max(tagFrom, changedPosition);
accumulate_min(tagTo, changedEnd);
if (tagTo > tagFrom) {
accumulate_min(firstTagStart, tagFrom);
prepareFormattingOptimization(document);
if (applyNoTagFrom < tagFrom) {
removeTags(document, applyNoTagFrom, tagFrom);
}
QTextCursor c(document->docHandle(), tagFrom);
c.setPosition(tagTo, QTextCursor::KeepAnchor);
QTextCharFormat format;
format.setAnchor(true);
format.setAnchorName(tag.id);
format.setForeground(st::defaultTextStyle.linkFg);
c.mergeCharFormat(format);
applyNoTagFrom = tagTo;
}
}
if (applyNoTagFrom < changedEnd) {
removeTags(document, applyNoTagFrom, changedEnd);
}
return firstTagStart;
}
// When inserting a part of text inside a tag we need to have
// a way to know if the insertion replaced the end of the tag
// or it was strictly inside (in the middle) of the tag.
bool wasInsertTillTheEndOfTag(QTextBlock block, QTextBlock::iterator fragmentIt, int insertionEnd) {
auto insertTagName = fragmentIt.fragment().charFormat().anchorName();
while (true) {
int32 start = position, end = position + charsAdded;
QTextBlock from = doc->findBlock(start), till = doc->findBlock(end);
if (till.isValid()) till = till.next();
for (; !fragmentIt.atEnd(); ++fragmentIt) {
auto fragment = fragmentIt.fragment();
bool fragmentOutsideInsertion = (fragment.position() >= insertionEnd);
if (fragmentOutsideInsertion) {
return (fragment.charFormat().anchorName() != insertTagName);
}
int fragmentEnd = fragment.position() + fragment.length();
bool notFullFragmentInserted = (fragmentEnd > insertionEnd);
if (notFullFragmentInserted) {
return false;
}
}
if (block.isValid()) {
fragmentIt = block.begin();
block = block.next();
} else {
break;
}
}
// Insertion goes till the end of the text => not strictly inside a tag.
return true;
}
for (QTextBlock b = from; b != till; b = b.next()) {
for (QTextBlock::Iterator iter = b.begin(); !iter.atEnd(); ++iter) {
QTextFragment fragment(iter.fragment());
if (!fragment.isValid()) continue;
struct FormattingAction {
enum class Type {
Invalid,
InsertEmoji,
TildeFont,
RemoveTag,
};
Type type = Type::Invalid;
EmojiPtr emoji = nullptr;
bool isTilde = false;
int intervalStart = 0;
int intervalEnd = 0;
};
int32 fp = fragment.position(), fe = fp + fragment.length();
if (fp >= end || fe <= start) {
} // namespace
void FlatTextarea::processFormatting(int changedPosition, int changedEnd) {
// Tilde formatting.
auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold");
bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont);
bool isTildeFragment = false;
// First tag handling (the one we inserted text to).
bool startTagFound = false;
bool breakTagOnNotLetter = false;
auto doc = document();
// Apply inserted tags.
int breakTagOnNotLetterTill = processInsertedTags(doc, changedPosition, changedEnd, _insertedTags);
using ActionType = FormattingAction::Type;
while (true) {
FormattingAction action;
auto fromBlock = doc->findBlock(changedPosition);
auto tillBlock = doc->findBlock(changedEnd);
if (tillBlock.isValid()) tillBlock = tillBlock.next();
for (auto block = fromBlock; block != tillBlock; block = block.next()) {
for (auto fragmentIt = block.begin(); !fragmentIt.atEnd(); ++fragmentIt) {
auto fragment = fragmentIt.fragment();
t_assert(fragment.isValid());
int fragmentPosition = fragment.position();
if (changedPosition >= fragmentPosition + fragment.length()) {
continue;
}
if (checkTilde) {
wasTildeFragment = (fragment.charFormat().fontFamily() == semibold);
int changedPositionInFragment = changedPosition - fragmentPosition; // Can be negative.
int changedEndInFragment = changedEnd - fragmentPosition;
if (changedEndInFragment <= 0) {
break;
}
QString t(fragment.text());
const QChar *ch = t.constData(), *e = ch + t.size();
for (; ch != e; ++ch, ++fp) {
int emojiLen = 0;
emoji = emojiFromText(ch, e, &emojiLen);
if (emoji) {
if (replacePosition >= 0) {
emoji = 0; // replace tilde char format first
} else {
replacePosition = fp;
replaceLen = emojiLen;
auto charFormat = fragment.charFormat();
if (tildeFormatting) {
isTildeFragment = (charFormat.fontFamily() == semiboldFont);
}
auto fragmentText = fragment.text();
auto *textStart = fragmentText.constData();
auto *textEnd = textStart + fragmentText.size();
if (!startTagFound) {
startTagFound = true;
auto tagName = charFormat.anchorName();
if (!tagName.isEmpty()) {
breakTagOnNotLetter = wasInsertTillTheEndOfTag(block, fragmentIt, changedEnd);
}
}
auto *ch = textStart + qMax(changedPositionInFragment, 0);
for (; ch < textEnd; ++ch) {
int emojiLength = 0;
if (auto emoji = emojiFromText(ch, textEnd, &emojiLength)) {
// Replace emoji if no current action is prepared.
if (action.type == ActionType::Invalid) {
action.type = ActionType::InsertEmoji;
action.emoji = emoji;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = action.intervalStart + emojiLength;
}
break;
}
if (checkTilde && fp >= position) { // tilde fix in OpenSans
if (breakTagOnNotLetter && !ch->isLetter()) {
// Remove tag name till the end if no current action is prepared.
if (action.type != ActionType::Invalid) {
break;
}
breakTagOnNotLetter = false;
if (fragmentPosition + (ch - textStart) < breakTagOnNotLetterTill) {
action.type = ActionType::RemoveTag;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = breakTagOnNotLetterTill;
break;
}
}
if (tildeFormatting) { // Tilde symbol fix in OpenSans.
bool tilde = (ch->unicode() == '~');
if ((tilde && !wasTildeFragment) || (!tilde && wasTildeFragment)) {
if (replacePosition < 0) {
replacePosition = fp;
replaceLen = 1;
if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) {
if (action.type == ActionType::Invalid) {
action.type = ActionType::TildeFont;
action.intervalStart = fragmentPosition + (ch - textStart);
action.intervalEnd = action.intervalStart + 1;
action.isTilde = tilde;
} else {
++replaceLen;
++action.intervalEnd;
}
} else if (replacePosition >= 0) {
} else if (action.type == ActionType::TildeFont) {
break;
}
}
if (ch + 1 < e && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) {
if (ch + 1 < textEnd && ch->isHighSurrogate() && (ch + 1)->isLowSurrogate()) {
++ch;
++fp;
++fragmentPosition;
}
}
if (replacePosition >= 0) break;
if (action.type != ActionType::Invalid) break;
}
if (replacePosition >= 0) break;
if (action.type != ActionType::Invalid) break;
}
if (replacePosition >= 0) {
// Optimization: with null page size document does not re-layout
// on each insertText / mergeCharFormat.
if (!document()->pageSize().isNull()) {
document()->setPageSize(QSizeF(0, 0));
}
if (action.type != ActionType::Invalid) {
prepareFormattingOptimization(doc);
QTextCursor c(doc->docHandle(), replacePosition);
c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor);
if (emoji) {
insertEmoji(emoji, c);
} else {
QTextCursor c(doc->docHandle(), action.intervalStart);
c.setPosition(action.intervalEnd, QTextCursor::KeepAnchor);
if (action.type == ActionType::InsertEmoji) {
insertEmoji(action.emoji, c);
changedPosition = action.intervalStart + 1;
} else if (action.type == ActionType::RemoveTag) {
QTextCharFormat format;
format.setFontFamily(wasTildeFragment ? regular : semibold);
format.setAnchor(false);
format.setAnchorName(QString());
format.setForeground(st::black);
c.mergeCharFormat(format);
} else if (action.type == ActionType::TildeFont) {
QTextCharFormat format;
format.setFontFamily(action.isTilde ? semiboldFont : regularFont);
c.mergeCharFormat(format);
changedPosition = action.intervalEnd;
}
charsAdded -= replacePosition + replaceLen - position;
position = replacePosition + (emoji ? 1 : replaceLen);
emoji = nullptr;
replacePosition = -1;
} else {
break;
}
@ -928,6 +1080,9 @@ void FlatTextarea::processDocumentContentsChange(int position, int charsAdded) {
void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int charsAdded) {
if (_correcting) return;
int insertPosition = (_realInsertPosition >= 0) ? _realInsertPosition : position;
int insertLength = (_realInsertPosition >= 0) ? _realCharsAdded : charsAdded;
QTextCursor(document()->docHandle(), 0).joinPreviousEditBlock();
_correcting = true;
@ -936,18 +1091,18 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
c.movePosition(QTextCursor::End);
int32 fullSize = c.position(), toRemove = fullSize - _maxLength;
if (toRemove > 0) {
if (toRemove > charsAdded) {
if (charsAdded) {
c.setPosition(position);
c.setPosition((position + charsAdded), QTextCursor::KeepAnchor);
if (toRemove > insertLength) {
if (insertLength) {
c.setPosition(insertPosition);
c.setPosition((insertPosition + insertLength), QTextCursor::KeepAnchor);
c.removeSelectedText();
}
c.setPosition(fullSize - (toRemove - charsAdded));
c.setPosition(fullSize - (toRemove - insertLength));
c.setPosition(fullSize, QTextCursor::KeepAnchor);
c.removeSelectedText();
} else {
c.setPosition(position + (charsAdded - toRemove));
c.setPosition(position + charsAdded, QTextCursor::KeepAnchor);
c.setPosition(insertPosition + (insertLength - toRemove));
c.setPosition(insertPosition + insertLength, QTextCursor::KeepAnchor);
c.removeSelectedText();
}
}
@ -957,10 +1112,10 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
if (!_links.isEmpty()) {
bool changed = false;
for (auto i = _links.begin(); i != _links.end();) {
if (i->first + i->second <= position) {
if (i->first + i->second <= insertPosition) {
++i;
} else if (i->first >= position + charsRemoved) {
i->first += charsAdded - charsRemoved;
} else if (i->first >= insertPosition + charsRemoved) {
i->first += insertLength - charsRemoved;
++i;
} else {
i = _links.erase(i);
@ -975,24 +1130,16 @@ void FlatTextarea::onDocumentContentsChange(int position, int charsRemoved, int
return;
}
const int takeBack = 3;
position -= takeBack;
charsAdded += takeBack;
if (position < 0) {
charsAdded += position;
position = 0;
}
if (charsAdded <= 0) {
if (insertLength <= 0) {
QTextCursor(document()->docHandle(), 0).endEditBlock();
return;
}
_correcting = true;
QSizeF s = document()->pageSize();
processDocumentContentsChange(position, charsAdded);
if (document()->pageSize() != s) {
document()->setPageSize(s);
auto pageSize = document()->pageSize();
processFormatting(insertPosition, insertPosition + insertLength);
if (document()->pageSize() != pageSize) {
document()->setPageSize(pageSize);
}
_correcting = false;
@ -1164,6 +1311,9 @@ void FlatTextarea::dropEvent(QDropEvent *e) {
_inDrop = true;
QTextEdit::dropEvent(e);
_inDrop = false;
_insertedTags.clear();
_realInsertPosition = -1;
emit spacedReturnedPasted();
}

View File

@ -143,7 +143,16 @@ private:
QString getText(int start, int end, TagList *outTagsList, bool *outTagsChanged) const;
void getSingleEmojiFragment(QString &text, QTextFragment &fragment) const;
void processDocumentContentsChange(int position, int charsAdded);
// After any characters added we must postprocess them. This includes:
// 1. Replacing font family to semibold for ~ characters, if we used Open Sans 13px.
// 2. Replacing font family from semibold for all non-~ characters, if we used ...
// 3. Replacing emoji code sequences by ObjectReplacementCharacters with emoji pics.
// 4. Interrupting tags in which the text was inserted by any char except a letter.
// 5. Applying tags from "_insertedTags" in case we pasted text with tags, not just text.
// Rule 4 applies only if we inserted chars not in the middle of a tag (but at the end).
void processFormatting(int changedPosition, int changedEnd);
bool heightAutoupdated();
int _minHeight = -1; // < 0 - no autosize
@ -161,7 +170,12 @@ private:
Animation _a_appearance;
// Tags list which we should apply while setText() call or insert from mime data.
TagList _settingTags;
TagList _insertedTags;
// Override insert position and charsAdded from complex text editing
// (like drag-n-drop in the same text edit field).
int _realInsertPosition = -1;
int _realCharsAdded = 0;
style::flatTextarea _st;

View File

@ -253,7 +253,8 @@ public:
return result;
}
void checkEntities() {
// Returns true if at least one entity was parsed in the current position.
bool checkEntities() {
while (!removeFlags.isEmpty() && (ptr >= removeFlags.firstKey() || ptr >= end)) {
const QList<int32> &removing(removeFlags.first());
for (int32 i = removing.size(); i > 0;) {
@ -272,7 +273,7 @@ public:
++waitingEntity;
}
if (waitingEntity == entitiesEnd || ptr < start + waitingEntity->offset()) {
return;
return false;
}
int32 startFlags = 0;
@ -348,6 +349,7 @@ public:
} else {
while (waitingEntity != entitiesEnd && waitingEntity->length() <= 0) ++waitingEntity;
}
return true;
}
bool readSkipBlockCommand() {
@ -620,11 +622,7 @@ public:
lastSkipped = false;
checkTilde = !cRetina() && _t->_font->size() == 13 && _t->_font->flags() == 0; // tilde Open Sans fix
for (; ptr <= end; ++ptr) {
checkEntities();
if (rich) {
if (checkCommand()) {
checkEntities();
}
while (checkEntities() || (rich && checkCommand())) {
}
parseCurrentChar();
parseEmojiFromCurrent();