Support instant-replacement with a custom emoji.
This commit is contained in:
parent
1d34c64da8
commit
0daf3d4ac7
2 changed files with 56 additions and 17 deletions
|
|
@ -35,7 +35,6 @@ constexpr auto kInstantReplaceWhatId = QTextFormat::UserProperty + 1;
|
||||||
constexpr auto kInstantReplaceWithId = QTextFormat::UserProperty + 2;
|
constexpr auto kInstantReplaceWithId = QTextFormat::UserProperty + 2;
|
||||||
constexpr auto kReplaceTagId = QTextFormat::UserProperty + 3;
|
constexpr auto kReplaceTagId = QTextFormat::UserProperty + 3;
|
||||||
constexpr auto kTagProperty = QTextFormat::UserProperty + 4;
|
constexpr auto kTagProperty = QTextFormat::UserProperty + 4;
|
||||||
constexpr auto kCustomEmojiFormat = QTextFormat::UserObject + 1;
|
|
||||||
constexpr auto kCustomEmojiText = QTextFormat::UserProperty + 5;
|
constexpr auto kCustomEmojiText = QTextFormat::UserProperty + 5;
|
||||||
constexpr auto kCustomEmojiLink = QTextFormat::UserProperty + 6;
|
constexpr auto kCustomEmojiLink = QTextFormat::UserProperty + 6;
|
||||||
constexpr auto kCustomEmojiId = QTextFormat::UserProperty + 7;
|
constexpr auto kCustomEmojiId = QTextFormat::UserProperty + 7;
|
||||||
|
|
@ -50,6 +49,7 @@ const auto &kTagStrikeOut = InputField::kTagStrikeOut;
|
||||||
const auto &kTagCode = InputField::kTagCode;
|
const auto &kTagCode = InputField::kTagCode;
|
||||||
const auto &kTagPre = InputField::kTagPre;
|
const auto &kTagPre = InputField::kTagPre;
|
||||||
const auto &kTagSpoiler = InputField::kTagSpoiler;
|
const auto &kTagSpoiler = InputField::kTagSpoiler;
|
||||||
|
const auto &kCustomEmojiFormat = InputField::kCustomEmojiFormat;
|
||||||
const auto kTagCheckLinkMeta = u"^:/:/:^"_q;
|
const auto kTagCheckLinkMeta = u"^:/:/:^"_q;
|
||||||
const auto kNewlineChars = QString("\r\n")
|
const auto kNewlineChars = QString("\r\n")
|
||||||
+ QChar(0xfdd0) // QTextBeginningOfFrame
|
+ QChar(0xfdd0) // QTextBeginningOfFrame
|
||||||
|
|
@ -942,6 +942,8 @@ const QString InputField::kTagCode = QStringLiteral("`");
|
||||||
const QString InputField::kTagPre = QStringLiteral("```");
|
const QString InputField::kTagPre = QStringLiteral("```");
|
||||||
const QString InputField::kTagSpoiler = QStringLiteral("||");
|
const QString InputField::kTagSpoiler = QStringLiteral("||");
|
||||||
const QString InputField::kCustomEmojiTagStart = u"custom-emoji://"_q;
|
const QString InputField::kCustomEmojiTagStart = u"custom-emoji://"_q;
|
||||||
|
const int InputField::kCustomEmojiFormat
|
||||||
|
= QTextFormat::UserObject + 1;
|
||||||
|
|
||||||
class InputField::Inner final : public QTextEdit {
|
class InputField::Inner final : public QTextEdit {
|
||||||
public:
|
public:
|
||||||
|
|
@ -1012,12 +1014,9 @@ void InsertCustomEmojiAtCursor(
|
||||||
format.setProperty(kCustomEmojiId, CustomEmojiIdFromLink(link));
|
format.setProperty(kCustomEmojiId, CustomEmojiIdFromLink(link));
|
||||||
format.setVerticalAlignment(QTextCharFormat::AlignBottom);
|
format.setVerticalAlignment(QTextCharFormat::AlignBottom);
|
||||||
ApplyTagFormat(format, currentFormat);
|
ApplyTagFormat(format, currentFormat);
|
||||||
auto existingTag = format.property(kTagProperty).toString();
|
format.setProperty(kTagProperty, TextUtilities::TagWithAdded(
|
||||||
auto existingTags = existingTag.isEmpty()
|
format.property(kTagProperty).toString(),
|
||||||
? QList<QStringView>()
|
unique));
|
||||||
: TextUtilities::SplitTags(existingTag);
|
|
||||||
existingTags.push_back(unique);
|
|
||||||
format.setProperty(kTagProperty, TextUtilities::JoinTag(existingTags));
|
|
||||||
cursor.insertText(kObjectReplacement, format);
|
cursor.insertText(kObjectReplacement, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3352,20 +3351,34 @@ void InputField::applyInstantReplace(
|
||||||
} else if (position < length) {
|
} else if (position < length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
commitInstantReplacement(position - length, position, with, what, true);
|
commitInstantReplacement(
|
||||||
}
|
position - length,
|
||||||
|
position,
|
||||||
void InputField::commitInstantReplacement(
|
with,
|
||||||
int from,
|
QString(),
|
||||||
int till,
|
what,
|
||||||
const QString &with) {
|
true);
|
||||||
commitInstantReplacement(from, till, with, std::nullopt, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InputField::commitInstantReplacement(
|
void InputField::commitInstantReplacement(
|
||||||
int from,
|
int from,
|
||||||
int till,
|
int till,
|
||||||
const QString &with,
|
const QString &with,
|
||||||
|
const QString &customEmojiData) {
|
||||||
|
commitInstantReplacement(
|
||||||
|
from,
|
||||||
|
till,
|
||||||
|
with,
|
||||||
|
customEmojiData,
|
||||||
|
std::nullopt,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InputField::commitInstantReplacement(
|
||||||
|
int from,
|
||||||
|
int till,
|
||||||
|
const QString &with,
|
||||||
|
const QString &customEmojiData,
|
||||||
std::optional<QString> checkOriginal,
|
std::optional<QString> checkOriginal,
|
||||||
bool checkIfInMonospace) {
|
bool checkIfInMonospace) {
|
||||||
const auto original = getTextWithTagsPart(from, till).text;
|
const auto original = getTextWithTagsPart(from, till).text;
|
||||||
|
|
@ -3388,17 +3401,32 @@ void InputField::commitInstantReplacement(
|
||||||
cursor.setPosition(from);
|
cursor.setPosition(from);
|
||||||
cursor.setPosition(till, QTextCursor::KeepAnchor);
|
cursor.setPosition(till, QTextCursor::KeepAnchor);
|
||||||
|
|
||||||
|
const auto link = customEmojiData.isEmpty()
|
||||||
|
? QString()
|
||||||
|
: CustomEmojiLink(customEmojiData);
|
||||||
|
const auto unique = link.isEmpty()
|
||||||
|
? QString()
|
||||||
|
: MakeUniqueCustomEmojiLink(link);
|
||||||
auto format = [&]() -> QTextCharFormat {
|
auto format = [&]() -> QTextCharFormat {
|
||||||
auto emojiLength = 0;
|
auto emojiLength = 0;
|
||||||
const auto emoji = Emoji::Find(with, &emojiLength);
|
const auto emoji = Emoji::Find(with, &emojiLength);
|
||||||
if (!emoji || with.size() != emojiLength) {
|
if (!emoji || with.size() != emojiLength) {
|
||||||
return _defaultCharFormat;
|
return _defaultCharFormat;
|
||||||
|
} else if (!customEmojiData.isEmpty()) {
|
||||||
|
auto result = QTextCharFormat();
|
||||||
|
result.setObjectType(kCustomEmojiFormat);
|
||||||
|
result.setProperty(kCustomEmojiText, with);
|
||||||
|
result.setProperty(kCustomEmojiLink, unique);
|
||||||
|
result.setProperty(kCustomEmojiId, CustomEmojiIdFromLink(link));
|
||||||
|
result.setVerticalAlignment(QTextCharFormat::AlignBottom);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
const auto use = Integration::Instance().defaultEmojiVariant(
|
const auto use = Integration::Instance().defaultEmojiVariant(
|
||||||
emoji);
|
emoji);
|
||||||
return PrepareEmojiFormat(use, _st.font);
|
return PrepareEmojiFormat(use, _st.font);
|
||||||
}();
|
}();
|
||||||
const auto replacement = format.isImageFormat()
|
const auto replacement = (format.isImageFormat()
|
||||||
|
|| format.objectType() == kCustomEmojiFormat)
|
||||||
? kObjectReplacement
|
? kObjectReplacement
|
||||||
: with;
|
: with;
|
||||||
format.setProperty(kInstantReplaceWhatId, original);
|
format.setProperty(kInstantReplaceWhatId, original);
|
||||||
|
|
@ -3407,6 +3435,11 @@ void InputField::commitInstantReplacement(
|
||||||
kInstantReplaceRandomId,
|
kInstantReplaceRandomId,
|
||||||
base::RandomValue<uint32>());
|
base::RandomValue<uint32>());
|
||||||
ApplyTagFormat(format, cursor.charFormat());
|
ApplyTagFormat(format, cursor.charFormat());
|
||||||
|
if (!unique.isEmpty()) {
|
||||||
|
format.setProperty(kTagProperty, TextUtilities::TagWithAdded(
|
||||||
|
format.property(kTagProperty).toString(),
|
||||||
|
unique));
|
||||||
|
}
|
||||||
cursor.insertText(replacement, format);
|
cursor.insertText(replacement, format);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -220,6 +220,7 @@ public:
|
||||||
static const QString kTagPre;
|
static const QString kTagPre;
|
||||||
static const QString kTagSpoiler;
|
static const QString kTagSpoiler;
|
||||||
static const QString kCustomEmojiTagStart;
|
static const QString kCustomEmojiTagStart;
|
||||||
|
static const int kCustomEmojiFormat;
|
||||||
|
|
||||||
InputField(
|
InputField(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
|
@ -301,7 +302,11 @@ public:
|
||||||
void setInstantReplacesEnabled(rpl::producer<bool> enabled);
|
void setInstantReplacesEnabled(rpl::producer<bool> enabled);
|
||||||
void setMarkdownReplacesEnabled(rpl::producer<bool> enabled);
|
void setMarkdownReplacesEnabled(rpl::producer<bool> enabled);
|
||||||
void setExtendedContextMenu(rpl::producer<ExtendedContextMenu> value);
|
void setExtendedContextMenu(rpl::producer<ExtendedContextMenu> value);
|
||||||
void commitInstantReplacement(int from, int till, const QString &with);
|
void commitInstantReplacement(
|
||||||
|
int from,
|
||||||
|
int till,
|
||||||
|
const QString &with,
|
||||||
|
const QString &customEmojiData);
|
||||||
void commitMarkdownLinkEdit(
|
void commitMarkdownLinkEdit(
|
||||||
EditLinkSelection selection,
|
EditLinkSelection selection,
|
||||||
const QString &text,
|
const QString &text,
|
||||||
|
|
@ -497,6 +502,7 @@ private:
|
||||||
int from,
|
int from,
|
||||||
int till,
|
int till,
|
||||||
const QString &with,
|
const QString &with,
|
||||||
|
const QString &customEmojiData,
|
||||||
std::optional<QString> checkOriginal,
|
std::optional<QString> checkOriginal,
|
||||||
bool checkIfInMonospace);
|
bool checkIfInMonospace);
|
||||||
bool commitMarkdownReplacement(
|
bool commitMarkdownReplacement(
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue