Partially (italic+colored) support blockquotes.
This commit is contained in:
parent
68b89a6ba9
commit
c317f2a353
12 changed files with 134 additions and 58 deletions
|
|
@ -148,6 +148,10 @@ QString Integration::phraseFormattingStrikeOut() {
|
||||||
return "Strike-through";
|
return "Strike-through";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString Integration::phraseFormattingBlockquote() {
|
||||||
|
return "Quote";
|
||||||
|
}
|
||||||
|
|
||||||
QString Integration::phraseFormattingMonospace() {
|
QString Integration::phraseFormattingMonospace() {
|
||||||
return "Monospace";
|
return "Monospace";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,6 +83,7 @@ public:
|
||||||
[[nodiscard]] virtual QString phraseFormattingItalic();
|
[[nodiscard]] virtual QString phraseFormattingItalic();
|
||||||
[[nodiscard]] virtual QString phraseFormattingUnderline();
|
[[nodiscard]] virtual QString phraseFormattingUnderline();
|
||||||
[[nodiscard]] virtual QString phraseFormattingStrikeOut();
|
[[nodiscard]] virtual QString phraseFormattingStrikeOut();
|
||||||
|
[[nodiscard]] virtual QString phraseFormattingBlockquote();
|
||||||
[[nodiscard]] virtual QString phraseFormattingMonospace();
|
[[nodiscard]] virtual QString phraseFormattingMonospace();
|
||||||
[[nodiscard]] virtual QString phraseFormattingSpoiler();
|
[[nodiscard]] virtual QString phraseFormattingSpoiler();
|
||||||
[[nodiscard]] virtual QString phraseButtonOk();
|
[[nodiscard]] virtual QString phraseButtonOk();
|
||||||
|
|
|
||||||
|
|
@ -1006,6 +1006,7 @@ TextForMimeData String::toText(
|
||||||
{ TextBlockFlag::StrikeOut, EntityType::StrikeOut },
|
{ TextBlockFlag::StrikeOut, EntityType::StrikeOut },
|
||||||
{ TextBlockFlag::Code, EntityType::Code }, // #TODO entities
|
{ TextBlockFlag::Code, EntityType::Code }, // #TODO entities
|
||||||
{ TextBlockFlag::Pre, EntityType::Pre },
|
{ TextBlockFlag::Pre, EntityType::Pre },
|
||||||
|
{ TextBlockFlag::Blockquote, EntityType::Blockquote },
|
||||||
} : std::vector<MarkdownTagTracker>();
|
} : std::vector<MarkdownTagTracker>();
|
||||||
const auto flagsChangeCallback = [&](int32 oldFlags, int32 newFlags) {
|
const auto flagsChangeCallback = [&](int32 oldFlags, int32 newFlags) {
|
||||||
if (!composeEntities) {
|
if (!composeEntities) {
|
||||||
|
|
|
||||||
|
|
@ -392,7 +392,9 @@ style::font WithFlags(
|
||||||
|| (fontFlags & FontSemibold)) {
|
|| (fontFlags & FontSemibold)) {
|
||||||
result = result->semibold();
|
result = result->semibold();
|
||||||
}
|
}
|
||||||
if ((flags & TextBlockFlag::Italic) || (fontFlags & FontItalic)) {
|
if ((flags & TextBlockFlag::Italic)
|
||||||
|
|| (fontFlags & FontItalic)
|
||||||
|
|| (flags & TextBlockFlag::Blockquote)) {
|
||||||
result = result->italic();
|
result = result->italic();
|
||||||
}
|
}
|
||||||
if ((flags & TextBlockFlag::Underline) || (fontFlags & FontUnderline)) {
|
if ((flags & TextBlockFlag::Underline) || (fontFlags & FontUnderline)) {
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,16 @@ enum class TextBlockType : uint16 {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class TextBlockFlag : uint16 {
|
enum class TextBlockFlag : uint16 {
|
||||||
Bold = 0x001,
|
Bold = 0x001,
|
||||||
Italic = 0x002,
|
Italic = 0x002,
|
||||||
Underline = 0x004,
|
Underline = 0x004,
|
||||||
StrikeOut = 0x008,
|
StrikeOut = 0x008,
|
||||||
Tilde = 0x010, // Tilde fix in OpenSans.
|
Tilde = 0x010, // Tilde fix in OpenSans.
|
||||||
Semibold = 0x020,
|
Semibold = 0x020,
|
||||||
Code = 0x040,
|
Code = 0x040,
|
||||||
Pre = 0x080,
|
Pre = 0x080,
|
||||||
Spoiler = 0x100,
|
Spoiler = 0x100,
|
||||||
|
Blockquote = 0x200,
|
||||||
};
|
};
|
||||||
inline constexpr bool is_flag_type(TextBlockFlag) { return true; }
|
inline constexpr bool is_flag_type(TextBlockFlag) { return true; }
|
||||||
using TextBlockFlags = base::flags<TextBlockFlag>;
|
using TextBlockFlags = base::flags<TextBlockFlag>;
|
||||||
|
|
|
||||||
|
|
@ -1438,7 +1438,9 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
|
||||||
if (s > half) {
|
if (s > half) {
|
||||||
bool inEntity = (currentEntity < entityCount) && (ch > start + left.entities[currentEntity].offset()) && (ch < start + left.entities[currentEntity].offset() + left.entities[currentEntity].length());
|
bool inEntity = (currentEntity < entityCount) && (ch > start + left.entities[currentEntity].offset()) && (ch < start + left.entities[currentEntity].offset() + left.entities[currentEntity].length());
|
||||||
EntityType entityType = (currentEntity < entityCount) ? left.entities[currentEntity].type() : EntityType::Invalid;
|
EntityType entityType = (currentEntity < entityCount) ? left.entities[currentEntity].type() : EntityType::Invalid;
|
||||||
bool canBreakEntity = (entityType == EntityType::Pre || entityType == EntityType::Code); // #TODO entities
|
bool canBreakEntity = (entityType == EntityType::Pre)
|
||||||
|
|| (entityType == EntityType::Blockquote)
|
||||||
|
|| (entityType == EntityType::Code); // #TODO entities
|
||||||
int32 noEntityLevel = inEntity ? 0 : 1;
|
int32 noEntityLevel = inEntity ? 0 : 1;
|
||||||
|
|
||||||
auto markGoodAsLevel = [&](int newLevel) {
|
auto markGoodAsLevel = [&](int newLevel) {
|
||||||
|
|
@ -1464,9 +1466,15 @@ bool CutPart(TextWithEntities &sending, TextWithEntities &left, int32 limit) {
|
||||||
}
|
}
|
||||||
} else if (ch + 1 < end && IsNewline(*(ch + 1))) {
|
} else if (ch + 1 < end && IsNewline(*(ch + 1))) {
|
||||||
markGoodAsLevel(15);
|
markGoodAsLevel(15);
|
||||||
} else if (currentEntity < entityCount && ch + 1 == start + left.entities[currentEntity].offset() && left.entities[currentEntity].type() == EntityType::Pre) {
|
} else if (currentEntity < entityCount
|
||||||
|
&& ch + 1 == start + left.entities[currentEntity].offset()
|
||||||
|
&& (left.entities[currentEntity].type() == EntityType::Pre
|
||||||
|
|| left.entities[currentEntity].type() == EntityType::Blockquote)) {
|
||||||
markGoodAsLevel(14);
|
markGoodAsLevel(14);
|
||||||
} else if (currentEntity > 0 && ch == start + left.entities[currentEntity - 1].offset() + left.entities[currentEntity - 1].length() && left.entities[currentEntity - 1].type() == EntityType::Pre) {
|
} else if (currentEntity > 0
|
||||||
|
&& ch == start + left.entities[currentEntity - 1].offset() + left.entities[currentEntity - 1].length()
|
||||||
|
&& (left.entities[currentEntity - 1].type() == EntityType::Pre
|
||||||
|
|| left.entities[currentEntity - 1].type() == EntityType::Blockquote)) {
|
||||||
markGoodAsLevel(14);
|
markGoodAsLevel(14);
|
||||||
} else {
|
} else {
|
||||||
markGoodAsLevel(13);
|
markGoodAsLevel(13);
|
||||||
|
|
@ -2029,6 +2037,7 @@ EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) {
|
||||||
EntityType::Spoiler,
|
EntityType::Spoiler,
|
||||||
EntityType::Code,
|
EntityType::Code,
|
||||||
EntityType::Pre,
|
EntityType::Pre,
|
||||||
|
EntityType::Blockquote,
|
||||||
};
|
};
|
||||||
struct State {
|
struct State {
|
||||||
QString link;
|
QString link;
|
||||||
|
|
@ -2146,6 +2155,8 @@ EntitiesInText ConvertTextTagsToEntities(const TextWithTags::Tags &tags) {
|
||||||
&& single.startsWith(Tags::kTagPre)) {
|
&& single.startsWith(Tags::kTagPre)) {
|
||||||
result.set(EntityType::Pre);
|
result.set(EntityType::Pre);
|
||||||
result.language = single.mid(languageStart).toString();
|
result.language = single.mid(languageStart).toString();
|
||||||
|
} else if (single == Tags::kTagBlockquote) {
|
||||||
|
result.set(EntityType::Blockquote);
|
||||||
} else if (single == Tags::kTagSpoiler) {
|
} else if (single == Tags::kTagSpoiler) {
|
||||||
result.set(EntityType::Spoiler);
|
result.set(EntityType::Spoiler);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -2253,6 +2264,9 @@ TextWithTags::Tags ConvertEntitiesToTextTags(
|
||||||
}
|
}
|
||||||
push(Ui::InputField::kTagPre);
|
push(Ui::InputField::kTagPre);
|
||||||
} break;
|
} break;
|
||||||
|
case EntityType::Blockquote:
|
||||||
|
push(Ui::InputField::kTagBlockquote);
|
||||||
|
break;
|
||||||
case EntityType::Spoiler: push(Ui::InputField::kTagSpoiler); break;
|
case EntityType::Spoiler: push(Ui::InputField::kTagSpoiler); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ enum class EntityType : uchar {
|
||||||
StrikeOut,
|
StrikeOut,
|
||||||
Code, // inline
|
Code, // inline
|
||||||
Pre, // block
|
Pre, // block
|
||||||
|
Blockquote,
|
||||||
Spoiler,
|
Spoiler,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,8 @@ constexpr auto kMaxDiacAfterSymbol = 2;
|
||||||
|| type == EntityType::Colorized
|
|| type == EntityType::Colorized
|
||||||
|| type == EntityType::Spoiler
|
|| type == EntityType::Spoiler
|
||||||
|| type == EntityType::Code
|
|| type == EntityType::Code
|
||||||
|| type == EntityType::Pre))) {
|
|| type == EntityType::Pre
|
||||||
|
|| type == EntityType::Blockquote))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
result.entities.push_back(preparsed.at(i));
|
result.entities.push_back(preparsed.at(i));
|
||||||
|
|
@ -226,6 +227,17 @@ void Parser::createNewlineBlock() {
|
||||||
createBlock();
|
createBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Parser::ensureAtNewline() {
|
||||||
|
const auto lastType = _t->_blocks.empty()
|
||||||
|
? TextBlockType::Newline
|
||||||
|
: _t->_blocks.back()->type();
|
||||||
|
if (lastType != TextBlockType::Newline) {
|
||||||
|
auto saved = base::take(_customEmojiData);
|
||||||
|
createNewlineBlock();
|
||||||
|
_customEmojiData = base::take(saved);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Parser::finishEntities() {
|
void Parser::finishEntities() {
|
||||||
while (!_startedEntities.empty()
|
while (!_startedEntities.empty()
|
||||||
&& (_ptr >= _startedEntities.begin()->first || _ptr >= _end)) {
|
&& (_ptr >= _startedEntities.begin()->first || _ptr >= _end)) {
|
||||||
|
|
@ -239,9 +251,13 @@ void Parser::finishEntities() {
|
||||||
if (_flags & (*flags)) {
|
if (_flags & (*flags)) {
|
||||||
createBlock();
|
createBlock();
|
||||||
_flags &= ~(*flags);
|
_flags &= ~(*flags);
|
||||||
if (((*flags) & TextBlockFlag::Pre)
|
const auto lastType = _t->_blocks.empty()
|
||||||
&& !_t->_blocks.empty()
|
? TextBlockType::Newline
|
||||||
&& _t->_blocks.back()->type() != TextBlockType::Newline) {
|
: _t->_blocks.back()->type();
|
||||||
|
if ((lastType != TextBlockType::Newline)
|
||||||
|
&& ((*flags)
|
||||||
|
& (TextBlockFlag::Pre
|
||||||
|
| TextBlockFlag::Blockquote))) {
|
||||||
_newlineAwaited = true;
|
_newlineAwaited = true;
|
||||||
}
|
}
|
||||||
if (IsMono(*flags)) {
|
if (IsMono(*flags)) {
|
||||||
|
|
@ -320,11 +336,7 @@ bool Parser::checkEntities() {
|
||||||
} else {
|
} else {
|
||||||
flags = TextBlockFlag::Pre;
|
flags = TextBlockFlag::Pre;
|
||||||
createBlock();
|
createBlock();
|
||||||
if (!_t->_blocks.empty()
|
ensureAtNewline();
|
||||||
&& _t->_blocks.back()->type() != TextBlockType::Newline
|
|
||||||
&& _customEmojiData.isEmpty()) {
|
|
||||||
createNewlineBlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
const auto text = QString(entityBegin, entityLength);
|
const auto text = QString(entityBegin, entityLength);
|
||||||
|
|
||||||
|
|
@ -338,6 +350,10 @@ bool Parser::checkEntities() {
|
||||||
_monos.push_back({ .text = text, .type = entityType });
|
_monos.push_back({ .text = text, .type = entityType });
|
||||||
monoIndex = _monos.size();
|
monoIndex = _monos.size();
|
||||||
}
|
}
|
||||||
|
} else if (entityType == EntityType::Blockquote) {
|
||||||
|
flags = TextBlockFlag::Blockquote;
|
||||||
|
createBlock();
|
||||||
|
ensureAtNewline();
|
||||||
} else if (entityType == EntityType::Url
|
} else if (entityType == EntityType::Url
|
||||||
|| entityType == EntityType::Email
|
|| entityType == EntityType::Email
|
||||||
|| entityType == EntityType::Mention
|
|| entityType == EntityType::Mention
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ private:
|
||||||
void blockCreated();
|
void blockCreated();
|
||||||
void createBlock(int32 skipBack = 0);
|
void createBlock(int32 skipBack = 0);
|
||||||
void createNewlineBlock();
|
void createNewlineBlock();
|
||||||
|
void ensureAtNewline();
|
||||||
|
|
||||||
// Returns true if at least one entity was parsed in the current position.
|
// Returns true if at least one entity was parsed in the current position.
|
||||||
bool checkEntities();
|
bool checkEntities();
|
||||||
|
|
|
||||||
|
|
@ -2028,7 +2028,7 @@ void Renderer::applyBlockProperties(const AbstractBlock *block) {
|
||||||
_currentPen = &_originalPen;
|
_currentPen = &_originalPen;
|
||||||
_currentPenSelected = &_originalPenSelected;
|
_currentPenSelected = &_originalPenSelected;
|
||||||
}
|
}
|
||||||
} else if (isMono) {
|
} else if (isMono || (flags & TextBlockFlag::Blockquote)) {
|
||||||
_currentPen = &_palette->monoFg->p;
|
_currentPen = &_palette->monoFg->p;
|
||||||
_currentPenSelected = &_palette->selectMonoFg->p;
|
_currentPenSelected = &_palette->selectMonoFg->p;
|
||||||
} else if (block->linkIndex()) {
|
} else if (block->linkIndex()) {
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ const auto &kTagUnderline = InputField::kTagUnderline;
|
||||||
const auto &kTagStrikeOut = InputField::kTagStrikeOut;
|
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 &kTagBlockquote = InputField::kTagBlockquote;
|
||||||
const auto &kTagSpoiler = InputField::kTagSpoiler;
|
const auto &kTagSpoiler = InputField::kTagSpoiler;
|
||||||
const auto &kCustomEmojiFormat = InputField::kCustomEmojiFormat;
|
const auto &kCustomEmojiFormat = InputField::kCustomEmojiFormat;
|
||||||
const auto kTagCheckLinkMeta = u"^:/:/:^"_q;
|
const auto kTagCheckLinkMeta = u"^:/:/:^"_q;
|
||||||
|
|
@ -754,6 +755,9 @@ QTextCharFormat PrepareTagFormat(
|
||||||
font = font->underline();
|
font = font->underline();
|
||||||
} else if (tag == kTagStrikeOut) {
|
} else if (tag == kTagStrikeOut) {
|
||||||
font = font->strikeout();
|
font = font->strikeout();
|
||||||
|
} else if (tag == kTagBlockquote) {
|
||||||
|
color = st::defaultTextPalette.monoFg;
|
||||||
|
font = font->italic();
|
||||||
} else if (tag == kTagCode || IsTagPre(tag)) {
|
} else if (tag == kTagCode || IsTagPre(tag)) {
|
||||||
color = st::defaultTextPalette.monoFg;
|
color = st::defaultTextPalette.monoFg;
|
||||||
font = font->monospace();
|
font = font->monospace();
|
||||||
|
|
@ -931,13 +935,14 @@ struct FormattingAction {
|
||||||
|
|
||||||
// kTagUnderline is not used for Markdown.
|
// kTagUnderline is not used for Markdown.
|
||||||
|
|
||||||
const QString InputField::kTagBold = QStringLiteral("**");
|
const QString InputField::kTagBold = u"**"_q;
|
||||||
const QString InputField::kTagItalic = QStringLiteral("__");
|
const QString InputField::kTagItalic = u"__"_q;
|
||||||
const QString InputField::kTagUnderline = QStringLiteral("^^");
|
const QString InputField::kTagUnderline = u"^^"_q;
|
||||||
const QString InputField::kTagStrikeOut = QStringLiteral("~~");
|
const QString InputField::kTagStrikeOut = u"~~"_q;
|
||||||
const QString InputField::kTagCode = QStringLiteral("`");
|
const QString InputField::kTagCode = u"`"_q;
|
||||||
const QString InputField::kTagPre = QStringLiteral("```");
|
const QString InputField::kTagPre = u"```"_q;
|
||||||
const QString InputField::kTagSpoiler = QStringLiteral("||");
|
const QString InputField::kTagSpoiler = u"||"_q;
|
||||||
|
const QString InputField::kTagBlockquote = u">"_q;
|
||||||
const QString InputField::kCustomEmojiTagStart = u"custom-emoji://"_q;
|
const QString InputField::kCustomEmojiTagStart = u"custom-emoji://"_q;
|
||||||
const int InputField::kCustomEmojiFormat
|
const int InputField::kCustomEmojiFormat
|
||||||
= QTextFormat::UserObject + 1;
|
= QTextFormat::UserObject + 1;
|
||||||
|
|
@ -2903,6 +2908,14 @@ bool InputField::handleMarkdownKey(QKeyEvent *e) {
|
||||||
const auto events = QKeySequence(searchKey);
|
const auto events = QKeySequence(searchKey);
|
||||||
return sequence.matches(events) == QKeySequence::ExactMatch;
|
return sequence.matches(events) == QKeySequence::ExactMatch;
|
||||||
};
|
};
|
||||||
|
const auto matchesCtrlShiftDot = [&] {
|
||||||
|
// We can't match ctrl+shift+. with QKeySequence because
|
||||||
|
// shift+. gives us '>' and ctrl+shift+> is not the same.
|
||||||
|
// So we check by nativeVirtualKey instead.
|
||||||
|
return e->modifiers().testFlag(Qt::ControlModifier)
|
||||||
|
&& e->modifiers().testFlag(Qt::ShiftModifier)
|
||||||
|
&& (e->nativeVirtualKey() == 190);
|
||||||
|
};
|
||||||
if (e == QKeySequence::Bold) {
|
if (e == QKeySequence::Bold) {
|
||||||
toggleSelectionMarkdown(kTagBold);
|
toggleSelectionMarkdown(kTagBold);
|
||||||
} else if (e == QKeySequence::Italic) {
|
} else if (e == QKeySequence::Italic) {
|
||||||
|
|
@ -2913,6 +2926,8 @@ bool InputField::handleMarkdownKey(QKeyEvent *e) {
|
||||||
toggleSelectionMarkdown(kTagStrikeOut);
|
toggleSelectionMarkdown(kTagStrikeOut);
|
||||||
} else if (matches(kMonospaceSequence)) {
|
} else if (matches(kMonospaceSequence)) {
|
||||||
toggleSelectionMarkdown(kTagCode);
|
toggleSelectionMarkdown(kTagCode);
|
||||||
|
} else if (matches(kBlockquoteSequence) || matchesCtrlShiftDot()) {
|
||||||
|
toggleSelectionMarkdown(kTagBlockquote);
|
||||||
} else if (matches(kSpoilerSequence)) {
|
} else if (matches(kSpoilerSequence)) {
|
||||||
toggleSelectionMarkdown(kTagSpoiler);
|
toggleSelectionMarkdown(kTagSpoiler);
|
||||||
} else if (matches(kClearFormatSequence)) {
|
} else if (matches(kClearFormatSequence)) {
|
||||||
|
|
@ -3518,9 +3533,9 @@ void InputField::commitMarkdownLinkEdit(
|
||||||
void InputField::toggleSelectionMarkdown(const QString &tag) {
|
void InputField::toggleSelectionMarkdown(const QString &tag) {
|
||||||
_reverseMarkdownReplacement = false;
|
_reverseMarkdownReplacement = false;
|
||||||
const auto cursor = textCursor();
|
const auto cursor = textCursor();
|
||||||
const auto position = cursor.position();
|
auto position = cursor.position();
|
||||||
const auto from = cursor.selectionStart();
|
auto from = cursor.selectionStart();
|
||||||
const auto till = cursor.selectionEnd();
|
auto till = cursor.selectionEnd();
|
||||||
if (from == till) {
|
if (from == till) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -3529,33 +3544,50 @@ void InputField::toggleSelectionMarkdown(const QString &tag) {
|
||||||
} else if (HasFullTextTag(getTextWithTagsSelected(), tag)) {
|
} else if (HasFullTextTag(getTextWithTagsSelected(), tag)) {
|
||||||
removeMarkdownTag(from, till, tag);
|
removeMarkdownTag(from, till, tag);
|
||||||
} else {
|
} else {
|
||||||
const auto useTag = [&] {
|
const auto leftForBlock = [&] {
|
||||||
if (tag != kTagCode) {
|
if (!from) {
|
||||||
return tag;
|
return true;
|
||||||
}
|
}
|
||||||
const auto leftForBlock = [&] {
|
const auto text = getTextWithTagsPart(
|
||||||
if (!from) {
|
from - 1,
|
||||||
return true;
|
from + 1
|
||||||
}
|
).text;
|
||||||
const auto text = getTextWithTagsPart(
|
return text.isEmpty()
|
||||||
from - 1,
|
|| IsNewline(text[0])
|
||||||
from + 1
|
|| IsNewline(text[text.size() - 1]);
|
||||||
).text;
|
|
||||||
return text.isEmpty()
|
|
||||||
|| IsNewline(text[0])
|
|
||||||
|| IsNewline(text[text.size() - 1]);
|
|
||||||
}();
|
|
||||||
const auto rightForBlock = [&] {
|
|
||||||
const auto text = getTextWithTagsPart(
|
|
||||||
till - 1,
|
|
||||||
till + 1
|
|
||||||
).text;
|
|
||||||
return text.isEmpty()
|
|
||||||
|| IsNewline(text[0])
|
|
||||||
|| IsNewline(text[text.size() - 1]);
|
|
||||||
}();
|
|
||||||
return (leftForBlock && rightForBlock) ? kTagPre : kTagCode;
|
|
||||||
}();
|
}();
|
||||||
|
const auto rightForBlock = [&] {
|
||||||
|
const auto text = getTextWithTagsPart(
|
||||||
|
till - 1,
|
||||||
|
till + 1
|
||||||
|
).text;
|
||||||
|
return text.isEmpty()
|
||||||
|
|| IsNewline(text[0])
|
||||||
|
|| IsNewline(text[text.size() - 1]);
|
||||||
|
}();
|
||||||
|
|
||||||
|
const auto useTag = (tag != kTagCode)
|
||||||
|
? tag
|
||||||
|
: (leftForBlock && rightForBlock)
|
||||||
|
? kTagPre
|
||||||
|
: kTagCode;
|
||||||
|
if (tag == kTagBlockquote) {
|
||||||
|
QTextCursor(document()).beginEditBlock();
|
||||||
|
if (!leftForBlock) {
|
||||||
|
auto copy = textCursor();
|
||||||
|
copy.setPosition(from);
|
||||||
|
copy.insertText(u"\n"_q);
|
||||||
|
++position;
|
||||||
|
++from;
|
||||||
|
++till;
|
||||||
|
}
|
||||||
|
if (!rightForBlock) {
|
||||||
|
auto copy = textCursor();
|
||||||
|
copy.setPosition(till);
|
||||||
|
copy.insertText(u"\n"_q);
|
||||||
|
}
|
||||||
|
QTextCursor(document()).endEditBlock();
|
||||||
|
}
|
||||||
addMarkdownTag(from, till, useTag);
|
addMarkdownTag(from, till, useTag);
|
||||||
}
|
}
|
||||||
auto restorePosition = textCursor();
|
auto restorePosition = textCursor();
|
||||||
|
|
@ -3738,6 +3770,7 @@ void InputField::addMarkdownActions(
|
||||||
addtag(integration.phraseFormattingItalic(), QKeySequence::Italic, kTagItalic);
|
addtag(integration.phraseFormattingItalic(), QKeySequence::Italic, kTagItalic);
|
||||||
addtag(integration.phraseFormattingUnderline(), QKeySequence::Underline, kTagUnderline);
|
addtag(integration.phraseFormattingUnderline(), QKeySequence::Underline, kTagUnderline);
|
||||||
addtag(integration.phraseFormattingStrikeOut(), kStrikeOutSequence, kTagStrikeOut);
|
addtag(integration.phraseFormattingStrikeOut(), kStrikeOutSequence, kTagStrikeOut);
|
||||||
|
addtag(integration.phraseFormattingBlockquote(), kBlockquoteSequence, kTagBlockquote);
|
||||||
addtag(integration.phraseFormattingMonospace(), kMonospaceSequence, kTagCode);
|
addtag(integration.phraseFormattingMonospace(), kMonospaceSequence, kTagCode);
|
||||||
addtag(integration.phraseFormattingSpoiler(), kSpoilerSequence, kTagSpoiler);
|
addtag(integration.phraseFormattingSpoiler(), kSpoilerSequence, kTagSpoiler);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ namespace Ui {
|
||||||
|
|
||||||
const auto kClearFormatSequence = QKeySequence("ctrl+shift+n");
|
const auto kClearFormatSequence = QKeySequence("ctrl+shift+n");
|
||||||
const auto kStrikeOutSequence = QKeySequence("ctrl+shift+x");
|
const auto kStrikeOutSequence = QKeySequence("ctrl+shift+x");
|
||||||
|
const auto kBlockquoteSequence = QKeySequence("ctrl+shift+.");
|
||||||
const auto kMonospaceSequence = QKeySequence("ctrl+shift+m");
|
const auto kMonospaceSequence = QKeySequence("ctrl+shift+m");
|
||||||
const auto kEditLinkSequence = QKeySequence("ctrl+k");
|
const auto kEditLinkSequence = QKeySequence("ctrl+k");
|
||||||
const auto kSpoilerSequence = QKeySequence("ctrl+shift+p");
|
const auto kSpoilerSequence = QKeySequence("ctrl+shift+p");
|
||||||
|
|
@ -133,6 +134,7 @@ public:
|
||||||
static const QString kTagCode;
|
static const QString kTagCode;
|
||||||
static const QString kTagPre;
|
static const QString kTagPre;
|
||||||
static const QString kTagSpoiler;
|
static const QString kTagSpoiler;
|
||||||
|
static const QString kTagBlockquote;
|
||||||
static const QString kCustomEmojiTagStart;
|
static const QString kCustomEmojiTagStart;
|
||||||
static const int kCustomEmojiFormat;
|
static const int kCustomEmojiFormat;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue