Compare commits
4 Commits
work/nvrwh
...
work/redst
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9c6db8e04 | ||
|
|
7550b2b000 | ||
|
|
daa66c4abc | ||
|
|
e0b229e040 |
@@ -109,34 +109,6 @@ TestCase {
|
||||
compare(spyCursor.count, 5);
|
||||
}
|
||||
|
||||
function test_longFixedChars(): void {
|
||||
textEdit.forceActiveFocus();
|
||||
testHelper.setFixedChars("111", "222");
|
||||
compare(textEdit.text, "111222");
|
||||
compare(textEdit.cursorPosition, 3);
|
||||
compare(spyCursor.count, 0);
|
||||
keyClick("b");
|
||||
compare(textEdit.text, "111b222");
|
||||
compare(textEdit.cursorPosition, 4);
|
||||
compare(spyCursor.count, 1);
|
||||
keyClick(Qt.Key_Left);
|
||||
compare(textEdit.text, "111b222");
|
||||
compare(textEdit.cursorPosition, 3);
|
||||
compare(spyCursor.count, 2);
|
||||
keyClick(Qt.Key_Left);
|
||||
compare(textEdit.text, "111b222");
|
||||
compare(textEdit.cursorPosition, 3);
|
||||
compare(spyCursor.count, 3);
|
||||
keyClick(Qt.Key_Right);
|
||||
compare(textEdit.text, "111b222");
|
||||
compare(textEdit.cursorPosition, 4);
|
||||
compare(spyCursor.count, 4);
|
||||
keyClick(Qt.Key_Right);
|
||||
compare(textEdit.text, "111b222");
|
||||
compare(textEdit.cursorPosition, 4);
|
||||
compare(spyCursor.count, 5);
|
||||
}
|
||||
|
||||
function test_document(): void {
|
||||
// We can't get to the QTextDocument from QML so we have to use a helper function.
|
||||
compare(testHelper.compareDocuments(textEdit.textDocument), true);
|
||||
|
||||
@@ -626,10 +626,10 @@ void TextHandlerTest::componentOutput_data()
|
||||
MessageComponent{MessageComponentType::Code, u"Some code"_s, QVariantMap{{u"class"_s, u"html"_s}}}};
|
||||
QTest::newRow("quote") << u"<p>Text</p>\n<blockquote>\n<p>blockquote</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Quote, u"\"blockquote\""_s, {}}};
|
||||
MessageComponent{MessageComponentType::Quote, u"“blockquote”"_s, {}}};
|
||||
QTest::newRow("multiple paragraph quote") << u"<blockquote>\n<p>blockquote</p>\n<p>next paragraph</p>\n</blockquote>"_s
|
||||
<< QList<MessageComponent>{
|
||||
MessageComponent{MessageComponentType::Quote, u"<p>\"blockquote</p>\n<p>next paragraph\"</p>"_s, {}}};
|
||||
MessageComponent{MessageComponentType::Quote, u"<p>“blockquote</p>\n<p>next paragraph”</p>"_s, {}}};
|
||||
QTest::newRow("no tag first paragraph") << u"Text\n<p>Text</p>"_s
|
||||
<< QList<MessageComponent>{MessageComponent{MessageComponentType::Text, u"Text"_s, {}},
|
||||
MessageComponent{MessageComponentType::Text, u"Text"_s, {}}};
|
||||
|
||||
@@ -135,23 +135,7 @@ QString ChatBarCache::relationMessage() const
|
||||
return {};
|
||||
}
|
||||
if (auto [event, _] = m_room->getEvent(m_relationId); event != nullptr) {
|
||||
return EventHandler::rawMessageBody(*event);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
QList<MessageComponent> ChatBarCache::relationComponents() const
|
||||
{
|
||||
if (!m_room) {
|
||||
qCWarning(ChatBar) << "ChatBarCache:" << __FUNCTION__ << "called after room was deleted";
|
||||
return {};
|
||||
}
|
||||
if (m_relationId.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
if (auto [event, _] = m_room->getEvent(m_relationId); event != nullptr) {
|
||||
TextHandler handler;
|
||||
return TextHandler().textComponents(EventHandler::rawMessageBody(*event), EventHandler::messageBodyInputFormat(*event), m_room, event);
|
||||
return EventHandler::markdownBody(event);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace Quotient
|
||||
class RoomMember;
|
||||
}
|
||||
|
||||
struct MessageComponent;
|
||||
class NeoChatRoom;
|
||||
|
||||
/**
|
||||
@@ -92,6 +91,13 @@ class ChatBarCache : public QObject
|
||||
*/
|
||||
Q_PROPERTY(bool relationAuthorIsPresent READ relationAuthorIsPresent NOTIFY relationAuthorIsPresentChanged)
|
||||
|
||||
/**
|
||||
* @brief The content of the related message.
|
||||
*
|
||||
* Will be QString() if no related message.
|
||||
*/
|
||||
Q_PROPERTY(QString relationMessage READ relationMessage NOTIFY relationIdChanged)
|
||||
|
||||
/**
|
||||
* @brief Whether the chat bar is replying in a thread.
|
||||
*/
|
||||
@@ -141,7 +147,6 @@ public:
|
||||
bool relationAuthorIsPresent() const;
|
||||
|
||||
QString relationMessage() const;
|
||||
QList<MessageComponent> relationComponents() const;
|
||||
|
||||
bool isThreaded() const;
|
||||
QString threadId() const;
|
||||
|
||||
@@ -188,7 +188,7 @@ void ChatMarkdownHelper::checkMarkdown(int position, int charsRemoved, int chars
|
||||
// This can happen when formatting is applied.
|
||||
if (charsAdded == charsRemoved) {
|
||||
return;
|
||||
} else if (m_textItem->isCompleting || charsRemoved > charsAdded || charsAdded - charsRemoved > 1) {
|
||||
} else if ((m_textItem->textFormat() && m_textItem->textFormat() == Qt::TextFormat::PlainText) || m_textItem->isCompleting || charsRemoved > charsAdded || charsAdded - charsRemoved > 1) {
|
||||
updatePosition(std::max(0, position - charsRemoved + charsAdded));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <QTextCursor>
|
||||
|
||||
#include <Kirigami/Platform/PlatformTheme>
|
||||
#include <qtextdocument.h>
|
||||
|
||||
#include "chatbarsyntaxhighlighter.h"
|
||||
#include "neochatroom.h"
|
||||
@@ -164,52 +165,23 @@ void ChatTextItemHelper::initialize()
|
||||
int finalCursorPos = cursor.position();
|
||||
if (doc->isEmpty() && !m_initialFragment.isEmpty()) {
|
||||
cursor.insertFragment(m_initialFragment);
|
||||
if (cursor.blockFormat().bottomMargin() > 0) {
|
||||
auto blockFormat = cursor.blockFormat();
|
||||
blockFormat.setBottomMargin(0);
|
||||
cursor.setBlockFormat(blockFormat);
|
||||
}
|
||||
finalCursorPos = cursor.position();
|
||||
}
|
||||
|
||||
if (!m_fixedStartChars.isEmpty()) {
|
||||
if (!m_fixedStartChars.isEmpty() && doc->characterAt(0) != m_fixedStartChars) {
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, m_fixedStartChars.length());
|
||||
if (cursor.selectedText() != m_fixedStartChars) {
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.insertText(m_fixedStartChars);
|
||||
finalCursorPos += m_fixedStartChars.length();
|
||||
}
|
||||
cursor.insertText(m_fixedStartChars);
|
||||
finalCursorPos += m_fixedStartChars.length();
|
||||
}
|
||||
|
||||
if (!m_fixedStartChars.isEmpty()) {
|
||||
if (!m_fixedStartChars.isEmpty() && doc->characterAt(doc->characterCount()) != m_fixedStartChars) {
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, m_fixedEndChars.length());
|
||||
if (cursor.selectedText() != m_fixedEndChars) {
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.insertText(m_fixedEndChars);
|
||||
}
|
||||
cursor.keepPositionOnInsert();
|
||||
cursor.insertText(m_fixedEndChars);
|
||||
}
|
||||
setCursorPosition(finalCursorPos);
|
||||
cursor.endEditBlock();
|
||||
|
||||
qWarning() << doc->toRawText();
|
||||
const auto blockProperties = cursor.blockFormat().properties();
|
||||
for (const auto &property : blockProperties.keys()) {
|
||||
qWarning() << static_cast<QTextFormat::Property>(property) << blockProperties[property];
|
||||
}
|
||||
const auto textProperties = cursor.charFormat().properties();
|
||||
for (const auto &property : textProperties.keys()) {
|
||||
qWarning() << static_cast<QTextFormat::Property>(property) << textProperties[property];
|
||||
}
|
||||
const auto currentList = cursor.currentList();
|
||||
if (currentList) {
|
||||
const auto listProperties = currentList->format().properties();
|
||||
for (const auto &property : listProperties.keys()) {
|
||||
qWarning() << static_cast<QTextFormat::Property>(property) << listProperties[property];
|
||||
}
|
||||
}
|
||||
|
||||
m_initializingChars = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -402,9 +402,9 @@ QString TextHandler::stripBlockTags(QString string, const QString &tagType) cons
|
||||
}
|
||||
|
||||
// This is not a normal quotation mark but U+201C
|
||||
string.insert(startQuotationIndex, u"\""_s);
|
||||
string.insert(startQuotationIndex, u'“');
|
||||
// This is U+201D
|
||||
string.insert(endQuotationIndex, u"\""_s);
|
||||
string.insert(endQuotationIndex, u'”');
|
||||
}
|
||||
|
||||
return string;
|
||||
|
||||
@@ -13,10 +13,8 @@
|
||||
#include "enums/chatbartype.h"
|
||||
#include "enums/messagecomponenttype.h"
|
||||
#include "enums/richformat.h"
|
||||
#include "messagecomponent.h"
|
||||
#include "messagecontentmodel.h"
|
||||
#include "neochatroom.h"
|
||||
#include "texthandler.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
@@ -53,18 +51,6 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
||||
textItem->setRoom(m_room);
|
||||
}
|
||||
}
|
||||
// We can't guarantee whether room or type is intialised first so we have to handle.
|
||||
if (!m_room || !unhandledTypeChange) {
|
||||
return;
|
||||
}
|
||||
connectCache(m_room->cacheForType(*unhandledTypeChange));
|
||||
unhandledTypeChange = std::nullopt;
|
||||
const auto newCache = m_room->cacheForType(m_type);
|
||||
if (newCache && newCache->isEditing()) {
|
||||
initializeEdit();
|
||||
return;
|
||||
}
|
||||
initializeFromCache();
|
||||
});
|
||||
connect(this, &ChatBarMessageContentModel::typeChanged, this, [this](ChatBarType::Type oldType) {
|
||||
for (const auto &component : std::as_const(m_components)) {
|
||||
@@ -73,15 +59,9 @@ ChatBarMessageContentModel::ChatBarMessageContentModel(QObject *parent)
|
||||
}
|
||||
}
|
||||
if (!m_room) {
|
||||
unhandledTypeChange = oldType;
|
||||
return;
|
||||
}
|
||||
connectCache(m_room->cacheForType(oldType));
|
||||
const auto newCache = m_room->cacheForType(m_type);
|
||||
if (newCache && newCache->isEditing()) {
|
||||
initializeEdit();
|
||||
return;
|
||||
}
|
||||
initializeFromCache();
|
||||
});
|
||||
connect(m_markdownHelper, &ChatMarkdownHelper::unhandledBlockFormat, this, &ChatBarMessageContentModel::insertStyleAtCursor);
|
||||
@@ -110,8 +90,9 @@ void ChatBarMessageContentModel::connectCache(ChatBarCache *oldCache)
|
||||
}
|
||||
const auto currentCache = m_room->cacheForType(m_type);
|
||||
updateReplyModel();
|
||||
refocusCurrentComponent();
|
||||
if (currentCache->isEditing()) {
|
||||
initializeEdit();
|
||||
initializeFromCache();
|
||||
}
|
||||
});
|
||||
connect(m_room->cacheForType(m_type), &ChatBarCache::attachmentPathChanged, this, [this]() {
|
||||
@@ -171,38 +152,6 @@ void ChatBarMessageContentModel::initializeFromCache()
|
||||
Q_EMIT focusRowChanged();
|
||||
}
|
||||
|
||||
void ChatBarMessageContentModel::initializeEdit()
|
||||
{
|
||||
clearModel();
|
||||
|
||||
const auto currentCache = m_room->cacheForType(m_type);
|
||||
auto components = currentCache->relationComponents();
|
||||
if (components.isEmpty()) {
|
||||
initializeModel();
|
||||
return;
|
||||
}
|
||||
|
||||
beginResetModel();
|
||||
std::ranges::for_each(components, [this](MessageComponent component) {
|
||||
if (MessageComponentType::isTextType(component.type)) {
|
||||
const auto textItemWrapper = new ChatTextItemHelper(this);
|
||||
const auto initialFragment = component.type == MessageComponentType::Code ? QTextDocumentFragment::fromPlainText(component.display)
|
||||
: QTextDocumentFragment::fromHtml(component.display);
|
||||
textItemWrapper->setInitialFragment(initialFragment);
|
||||
textItemWrapper->setRoom(m_room);
|
||||
textItemWrapper->setType(m_type);
|
||||
if (component.type == MessageComponentType::Quote) {
|
||||
textItemWrapper->setFixedChars(u"\""_s, u"\""_s);
|
||||
}
|
||||
|
||||
component.attributes.insert(TextItemKey, QVariant::fromValue<ChatTextItemHelper *>(textItemWrapper));
|
||||
connectTextItem(textItemWrapper);
|
||||
}
|
||||
m_components += component;
|
||||
});
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
ChatBarType::Type ChatBarMessageContentModel::type() const
|
||||
{
|
||||
return m_type;
|
||||
|
||||
@@ -130,12 +130,10 @@ Q_SIGNALS:
|
||||
|
||||
private:
|
||||
ChatBarType::Type m_type = ChatBarType::None;
|
||||
std::optional<ChatBarType::Type> unhandledTypeChange = std::nullopt;
|
||||
void connectCache(ChatBarCache *oldCache = nullptr);
|
||||
|
||||
void initializeModel(const QString &initialText = {});
|
||||
void initializeFromCache();
|
||||
void initializeEdit();
|
||||
|
||||
std::optional<QString> getReplyEventId() override;
|
||||
|
||||
|
||||
@@ -30,6 +30,9 @@ SearchPage {
|
||||
*/
|
||||
property string senderId
|
||||
|
||||
// This requires client-side search we don't implement yet
|
||||
readonly property bool canSearch: !room.usesEncryption
|
||||
|
||||
title: i18nc("@action:title", "Search Messages")
|
||||
|
||||
model: SearchModel {
|
||||
@@ -45,6 +48,9 @@ SearchPage {
|
||||
searchFieldPlaceholder: i18n("Find messages…")
|
||||
noSearchPlaceholderMessage: i18n("Enter text to start searching")
|
||||
noResultPlaceholderMessage: i18n("No messages found")
|
||||
customPlaceholderIcon: "lock-symbolic"
|
||||
customPlaceholderText: !canSearch ? i18n("Cannot search in encrypted rooms") : ""
|
||||
enableSearch: canSearch
|
||||
|
||||
listVerticalLayoutDirection: ListView.BottomToTop
|
||||
|
||||
|
||||
Reference in New Issue
Block a user