Simplify and fix chat markdown helper and add additional tests
This commit is contained in:
@@ -77,8 +77,8 @@ TestCase {
|
|||||||
{tag: "inline code", input: "`c` ", outText: ["`", "c", "c`", "c "], outFormats: [[], [RichFormat.InlineCode], [RichFormat.InlineCode], []], unhandled: 0},
|
{tag: "inline code", input: "`c` ", outText: ["`", "c", "c`", "c "], outFormats: [[], [RichFormat.InlineCode], [RichFormat.InlineCode], []], unhandled: 0},
|
||||||
{tag: "code", input: "``` ", outText: ["`", "``", "```", " "], outFormats: [[], [], [], []], unhandled: 1},
|
{tag: "code", input: "``` ", outText: ["`", "``", "```", " "], outFormats: [[], [], [], []], unhandled: 1},
|
||||||
{tag: "strikethrough", input: "~~s~~ ", outText: ["~", "~~", "s", "s~", "s~~", "s "], outFormats: [[], [], [RichFormat.Strikethrough], [RichFormat.Strikethrough], [RichFormat.Strikethrough], []], unhandled: 0},
|
{tag: "strikethrough", input: "~~s~~ ", outText: ["~", "~~", "s", "s~", "s~~", "s "], outFormats: [[], [], [RichFormat.Strikethrough], [RichFormat.Strikethrough], [RichFormat.Strikethrough], []], unhandled: 0},
|
||||||
{tag: "underline", input: "__u__ ", outText: ["_", "__", "u", "u_", "u__", "u "], outFormats: [[], [], [RichFormat.Underline], [RichFormat.Underline], [RichFormat.Underline], []], unhandled: 0},
|
{tag: "underline", input: "_u_ ", outText: ["_", "u", "u_", "u "], outFormats: [[], [RichFormat.Underline], [RichFormat.Underline], []], unhandled: 0},
|
||||||
{tag: "multiple closable", input: "***__~~t~~__*** ", outText: ["*", "**", "*", "_", "__", "~", "~~", "t", "t~", "t~~", "t_", "t__", "t*", "t**", "t*", "t "], outFormats: [[], [], [RichFormat.Bold], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Italic], []], unhandled: 0},
|
{tag: "multiple closable", input: "***_~~t~~_*** ", outText: ["*", "**", "*", "_", "~", "~~", "t", "t~", "t~~", "t_", "t*", "t**", "t*", "t "], outFormats: [[], [], [RichFormat.Bold], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline, RichFormat.Strikethrough], [RichFormat.Bold, RichFormat.Italic, RichFormat.Underline], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Bold, RichFormat.Italic], [RichFormat.Italic], []], unhandled: 0},
|
||||||
{tag: "nonclosable closable", input: "* **b** ", outText: ["*", "* ", "*", "**", "b", "b*", "b**", "b "], outFormats: [[], [], [RichFormat.UnorderedList], [RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.UnorderedList]], unhandled: 0},
|
{tag: "nonclosable closable", input: "* **b** ", outText: ["*", "* ", "*", "**", "b", "b*", "b**", "b "], outFormats: [[], [], [RichFormat.UnorderedList], [RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.Bold, RichFormat.UnorderedList], [RichFormat.UnorderedList]], unhandled: 0},
|
||||||
{tag: "not at line start", input: " 1) ", outText: [" ", " 1", " 1)", " 1) "], outFormats: [[], [], [], []], unhandled: 0},
|
{tag: "not at line start", input: " 1) ", outText: [" ", " 1", " 1)", " 1) "], outFormats: [[], [], [], []], unhandled: 0},
|
||||||
]
|
]
|
||||||
@@ -96,4 +96,77 @@ TestCase {
|
|||||||
|
|
||||||
compare(spyUnhandledFormat.count, data.unhandled);
|
compare(spyUnhandledFormat.count, data.unhandled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function test_backspace(): void {
|
||||||
|
keyClick("*");
|
||||||
|
compare(chatMarkdownHelper.checkText("*"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
keyClick("*");
|
||||||
|
compare(chatMarkdownHelper.checkText("**"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
keyClick("b");
|
||||||
|
compare(chatMarkdownHelper.checkText("b"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick("o");
|
||||||
|
compare(chatMarkdownHelper.checkText("bo"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick("l");
|
||||||
|
compare(chatMarkdownHelper.checkText("bol"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick("d");
|
||||||
|
compare(chatMarkdownHelper.checkText("bold"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick(Qt.Key_Backspace);
|
||||||
|
compare(chatMarkdownHelper.checkText("bol"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick(Qt.Key_Backspace);
|
||||||
|
compare(chatMarkdownHelper.checkText("bo"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick("*");
|
||||||
|
compare(chatMarkdownHelper.checkText("bo*"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick("*");
|
||||||
|
compare(chatMarkdownHelper.checkText("bo**"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
keyClick(" ");
|
||||||
|
compare(chatMarkdownHelper.checkText("bo "), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_cursorMove(): void {
|
||||||
|
keyClick("t");
|
||||||
|
keyClick("e");
|
||||||
|
keyClick("s");
|
||||||
|
keyClick("t");
|
||||||
|
compare(chatMarkdownHelper.checkText("test"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
keyClick("*");
|
||||||
|
keyClick("*");
|
||||||
|
keyClick("b");
|
||||||
|
compare(chatMarkdownHelper.checkText("testb"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
textEdit.cursorPosition = 2;
|
||||||
|
keyClick("*");
|
||||||
|
keyClick("*");
|
||||||
|
keyClick("b");
|
||||||
|
compare(chatMarkdownHelper.checkText("tebstb"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test_insertText(): void {
|
||||||
|
textEdit.insert(0, "test");
|
||||||
|
compare(chatMarkdownHelper.checkText("test"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
textEdit.insert(4, "**b");
|
||||||
|
compare(chatMarkdownHelper.checkText("testb"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
|
||||||
|
textEdit.clear();
|
||||||
|
textEdit.insert(0, "test");
|
||||||
|
compare(chatMarkdownHelper.checkText("test"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([]), true);
|
||||||
|
textEdit.insert(2, "**b");
|
||||||
|
compare(chatMarkdownHelper.checkText("tebst"), true);
|
||||||
|
compare(chatMarkdownHelper.checkFormats([RichFormat.Bold]), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ public:
|
|||||||
if (!doc) {
|
if (!doc) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// qWarning() << doc->toPlainText() <<text;
|
||||||
return text == doc->toPlainText();
|
return text == doc->toPlainText();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +59,7 @@ public:
|
|||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// qWarning() << RichFormat::formatsAtCursor(cursor) <<formats;
|
||||||
return RichFormat::formatsAtCursor(cursor) == formats;
|
return RichFormat::formatsAtCursor(cursor) == formats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ QQC2.Control {
|
|||||||
|
|
||||||
height: Math.max(Math.min(chatScrollView.contentHeight + bottomPadding + topPadding, Kirigami.Units.gridUnit * 10), Kirigami.Units.gridUnit * 5)
|
height: Math.max(Math.min(chatScrollView.contentHeight + bottomPadding + topPadding, Kirigami.Units.gridUnit * 10), Kirigami.Units.gridUnit * 5)
|
||||||
leftPadding: rightPadding
|
leftPadding: rightPadding
|
||||||
rightPadding: (root.width - chatBarSizeHelper.availableWidth) / 2
|
rightPadding: (root.width - chatBarSizeHelper.availableWidth) / 2 + Kirigami.Units.largeSpacing
|
||||||
topPadding: Kirigami.Units.smallSpacing
|
topPadding: Kirigami.Units.smallSpacing
|
||||||
bottomPadding: Kirigami.Units.smallSpacing
|
bottomPadding: Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,20 @@ std::optional<bool> checkSequence(const QString ¤tString, const QString &n
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool checkSequenceBackwards(const QString ¤tString)
|
||||||
|
{
|
||||||
|
auto it = syntax.cbegin();
|
||||||
|
while ((it = std::find_if(it,
|
||||||
|
syntax.cend(),
|
||||||
|
[currentString](const MarkdownSyntax &syntax) {
|
||||||
|
return syntax.sequence.endsWith(currentString);
|
||||||
|
}))
|
||||||
|
!= syntax.cend()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<MarkdownSyntax> syntaxForSequence(const QString &sequence)
|
std::optional<MarkdownSyntax> syntaxForSequence(const QString &sequence)
|
||||||
{
|
{
|
||||||
const auto it = std::find_if(syntax.cbegin(), syntax.cend(), [sequence](const MarkdownSyntax &syntax) {
|
const auto it = std::find_if(syntax.cbegin(), syntax.cend(), [sequence](const MarkdownSyntax &syntax) {
|
||||||
@@ -118,75 +132,93 @@ void ChatMarkdownHelper::setTextItem(ChatTextItemHelper *textItem)
|
|||||||
|
|
||||||
void ChatMarkdownHelper::updateStart()
|
void ChatMarkdownHelper::updateStart()
|
||||||
{
|
{
|
||||||
m_startPos = *m_textItem->cursorPosition();
|
if (!m_textItem) {
|
||||||
m_endPos = m_startPos;
|
return;
|
||||||
if (m_startPos == 0) {
|
}
|
||||||
m_currentState = Pre;
|
const auto newCursorPosition = m_textItem->cursorPosition();
|
||||||
|
if (newCursorPosition) {
|
||||||
|
updatePosition(*newCursorPosition);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatMarkdownHelper::checkMarkdown(int position, int charsRemoved, int charsAdded)
|
void ChatMarkdownHelper::checkMarkdown(int position, int charsRemoved, int charsAdded)
|
||||||
{
|
{
|
||||||
|
updatePosition(position);
|
||||||
|
|
||||||
|
// This can happen when formatting is applied.
|
||||||
|
if (charsAdded == charsRemoved) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
auto cursor = m_textItem->textCursor();
|
auto cursor = m_textItem->textCursor();
|
||||||
if (cursor.isNull()) {
|
if (cursor.isNull()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (charsRemoved - charsAdded > 0) {
|
if (charsRemoved > charsAdded) {
|
||||||
if (position < m_startPos) {
|
updatePosition(std::max(0, position - charsRemoved + charsAdded));
|
||||||
m_startPos = position;
|
}
|
||||||
}
|
|
||||||
m_endPos -= charsRemoved;
|
checkMarkdownForward(charsAdded - charsRemoved);
|
||||||
cursor.setPosition(m_endPos);
|
}
|
||||||
cursor.setPosition(m_endPos + (cursor.atBlockEnd() ? 0 : 1), QTextCursor::KeepAnchor);
|
|
||||||
const auto nextChar = cursor.selectedText();
|
void ChatMarkdownHelper::updatePosition(int position)
|
||||||
m_currentState = m_startPos == 0 || nextChar == u' ' ? Pre : None;
|
{
|
||||||
|
if (position == m_endPos) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = 1; i <= charsAdded - charsRemoved; ++i) {
|
m_startPos = position;
|
||||||
|
m_endPos = position;
|
||||||
|
|
||||||
|
if (m_startPos <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto cursor = m_textItem->textCursor();
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cursor.setPosition(m_startPos);
|
||||||
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
||||||
|
while (checkSequenceBackwards(cursor.selectedText()) && m_startPos > 0) {
|
||||||
|
--m_startPos;
|
||||||
|
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ChatMarkdownHelper::checkMarkdownForward(int charsAdded)
|
||||||
|
{
|
||||||
|
if (charsAdded <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto cursor = m_textItem->textCursor();
|
||||||
|
if (cursor.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto i = 1; i <= charsAdded; ++i) {
|
||||||
cursor.setPosition(m_startPos);
|
cursor.setPosition(m_startPos);
|
||||||
|
const auto atBlockStart = cursor.atBlockStart();
|
||||||
cursor.setPosition(m_endPos, QTextCursor::KeepAnchor);
|
cursor.setPosition(m_endPos, QTextCursor::KeepAnchor);
|
||||||
const auto currentMarkdown = cursor.selectedText();
|
const auto currentMarkdown = cursor.selectedText();
|
||||||
cursor.setPosition(m_endPos);
|
cursor.setPosition(m_endPos);
|
||||||
cursor.setPosition(m_endPos + 1, QTextCursor::KeepAnchor);
|
cursor.setPosition(m_endPos + 1, QTextCursor::KeepAnchor);
|
||||||
const auto nextChar = cursor.selectedText();
|
const auto nextChar = cursor.selectedText();
|
||||||
cursor.setPosition(m_startPos);
|
|
||||||
|
|
||||||
const auto result = checkSequence(currentMarkdown, nextChar, cursor.atBlockStart());
|
const auto result = checkSequence(currentMarkdown, nextChar, atBlockStart);
|
||||||
|
if (!result) {
|
||||||
switch (m_currentState) {
|
|
||||||
case None:
|
|
||||||
if (nextChar == u' ' || cursor.atBlockEnd()) {
|
|
||||||
m_currentState = Pre;
|
|
||||||
}
|
|
||||||
++m_startPos;
|
++m_startPos;
|
||||||
m_endPos = m_startPos;
|
m_endPos = m_startPos;
|
||||||
break;
|
continue;
|
||||||
case Pre:
|
;
|
||||||
if (!result && RichFormat::formatsAtCursor(cursor).length() == 0) {
|
|
||||||
m_currentState = None;
|
|
||||||
} else if (result && !*result) {
|
|
||||||
m_currentState = Started;
|
|
||||||
++m_endPos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
++m_startPos;
|
|
||||||
m_endPos = m_startPos;
|
|
||||||
break;
|
|
||||||
case Started:
|
|
||||||
if (!result) {
|
|
||||||
m_currentState = Pre;
|
|
||||||
++m_startPos;
|
|
||||||
m_endPos = m_startPos;
|
|
||||||
break;
|
|
||||||
} else if (!*result) {
|
|
||||||
++m_endPos;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
complete();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
if (!*result) {
|
||||||
|
++m_endPos;
|
||||||
|
continue;
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
complete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,29 +232,24 @@ void ChatMarkdownHelper::complete()
|
|||||||
cursor.setPosition(m_startPos);
|
cursor.setPosition(m_startPos);
|
||||||
cursor.setPosition(m_endPos, QTextCursor::KeepAnchor);
|
cursor.setPosition(m_endPos, QTextCursor::KeepAnchor);
|
||||||
const auto syntax = syntaxForSequence(cursor.selectedText());
|
const auto syntax = syntaxForSequence(cursor.selectedText());
|
||||||
|
if (!syntax) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
cursor.removeSelectedText();
|
cursor.removeSelectedText();
|
||||||
|
|
||||||
if (m_currentFormats.contains(syntax->format)) {
|
|
||||||
m_currentFormats.remove(syntax->format);
|
|
||||||
} else if (syntax->closable) {
|
|
||||||
m_currentFormats.insert(syntax->format, m_startPos);
|
|
||||||
}
|
|
||||||
|
|
||||||
cursor.setPosition(m_startPos);
|
cursor.setPosition(m_startPos);
|
||||||
cursor.setPosition(m_startPos + 1, QTextCursor::KeepAnchor);
|
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
||||||
const auto nextChar = cursor.selectedText();
|
const auto nextChar = cursor.selectedText();
|
||||||
const auto result = checkSequence({}, nextChar, cursor.atBlockStart());
|
const auto result = checkSequence({}, nextChar, cursor.atBlockStart());
|
||||||
m_currentState = result ? Started : Pre;
|
|
||||||
|
|
||||||
// cursor.setPosition(m_startPos + 1);
|
cursor.setPosition(m_startPos);
|
||||||
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor);
|
cursor.movePosition(QTextCursor::NextWord, QTextCursor::KeepAnchor);
|
||||||
if (syntax) {
|
|
||||||
const auto formatType = RichFormat::typeForFormat(syntax->format);
|
const auto formatType = RichFormat::typeForFormat(syntax->format);
|
||||||
if (formatType == RichFormat::Block) {
|
if (formatType == RichFormat::Block) {
|
||||||
Q_EMIT unhandledBlockFormat(syntax->format);
|
Q_EMIT unhandledBlockFormat(syntax->format);
|
||||||
} else {
|
} else {
|
||||||
m_textItem->mergeFormatOnCursor(syntax->format, cursor);
|
m_textItem->mergeFormatOnCursor(syntax->format, cursor);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
m_startPos = result ? m_startPos : m_startPos + 1;
|
m_startPos = result ? m_startPos : m_startPos + 1;
|
||||||
@@ -231,14 +258,4 @@ void ChatMarkdownHelper::complete()
|
|||||||
cursor.endEditBlock();
|
cursor.endEditBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatMarkdownHelper::handleExternalFormatChange()
|
|
||||||
{
|
|
||||||
auto cursor = m_textItem->textCursor();
|
|
||||||
if (cursor.isNull()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
cursor.setPosition(m_startPos);
|
|
||||||
m_currentState = RichFormat::formatsAtCursor(cursor).length() > 0 ? Pre : None;
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "moc_chatmarkdownhelper.cpp"
|
#include "moc_chatmarkdownhelper.cpp"
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ public:
|
|||||||
ChatTextItemHelper *textItem() const;
|
ChatTextItemHelper *textItem() const;
|
||||||
void setTextItem(ChatTextItemHelper *textItem);
|
void setTextItem(ChatTextItemHelper *textItem);
|
||||||
|
|
||||||
void handleExternalFormatChange();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void textItemChanged();
|
void textItemChanged();
|
||||||
|
|
||||||
@@ -36,21 +34,14 @@ Q_SIGNALS:
|
|||||||
void unhandledBlockFormat(RichFormat::Format format);
|
void unhandledBlockFormat(RichFormat::Format format);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum State {
|
|
||||||
None,
|
|
||||||
Pre,
|
|
||||||
Started,
|
|
||||||
};
|
|
||||||
|
|
||||||
QPointer<ChatTextItemHelper> m_textItem;
|
QPointer<ChatTextItemHelper> m_textItem;
|
||||||
|
|
||||||
State m_currentState = None;
|
|
||||||
int m_startPos = 0;
|
int m_startPos = 0;
|
||||||
int m_endPos = 0;
|
int m_endPos = 0;
|
||||||
void updateStart();
|
void updateStart();
|
||||||
|
|
||||||
QHash<RichFormat::Format, int> m_currentFormats;
|
|
||||||
|
|
||||||
void checkMarkdown(int position, int charsRemoved, int charsAdded);
|
void checkMarkdown(int position, int charsRemoved, int charsAdded);
|
||||||
|
void updatePosition(int position);
|
||||||
|
void checkMarkdownForward(int charsAdded);
|
||||||
void complete();
|
void complete();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -160,6 +160,14 @@ bool RichFormat::hasFormat(QTextCursor cursor, Format format)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RichFormat::hasAnyFormat(QTextCursor cursor, QList<Format> formats)
|
||||||
|
{
|
||||||
|
for (const auto &format : formats) {
|
||||||
|
return hasFormat(cursor, format);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
QList<RichFormat::Format> RichFormat::formatsAtCursor(const QTextCursor &cursor)
|
QList<RichFormat::Format> RichFormat::formatsAtCursor(const QTextCursor &cursor)
|
||||||
{
|
{
|
||||||
QList<Format> formats;
|
QList<Format> formats;
|
||||||
|
|||||||
@@ -107,6 +107,13 @@ public:
|
|||||||
*/
|
*/
|
||||||
static bool hasFormat(QTextCursor cursor, Format format);
|
static bool hasFormat(QTextCursor cursor, Format format);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether the given QTextCursor has any of the given Formats.
|
||||||
|
*
|
||||||
|
* @sa Format, QTextCursor
|
||||||
|
*/
|
||||||
|
static bool hasAnyFormat(QTextCursor cursor, QList<Format> formats);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief A lsit of Formats on the given QTextCursor.
|
* @brief A lsit of Formats on the given QTextCursor.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user