Fix line strike.
Fix code block wrapping. Fix replyModel being garbage collected. Reply message in RoomPanelInput is richtext now.
This commit is contained in:
@@ -25,6 +25,26 @@ Rectangle {
|
|||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
color: parent.color
|
color: parent.color
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -39,6 +59,26 @@ Rectangle {
|
|||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
color: parent.color
|
color: parent.color
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -53,6 +93,26 @@ Rectangle {
|
|||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
color: parent.color
|
color: parent.color
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -67,5 +127,25 @@ Rectangle {
|
|||||||
antialiasing: true
|
antialiasing: true
|
||||||
|
|
||||||
color: parent.color
|
color: parent.color
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.right: parent.right
|
||||||
|
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
|
||||||
|
color: parent.color
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,7 +164,7 @@ RowLayout {
|
|||||||
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
||||||
contextMenu.saveFileAs.connect(saveFileAs)
|
contextMenu.saveFileAs.connect(saveFileAs)
|
||||||
contextMenu.reply.connect(function() {
|
contextMenu.reply.connect(function() {
|
||||||
roomPanelInput.replyModel = model
|
roomPanelInput.replyModel = Object.assign({}, model)
|
||||||
roomPanelInput.isReply = true
|
roomPanelInput.isReply = true
|
||||||
roomPanelInput.focus()
|
roomPanelInput.focus()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ RowLayout {
|
|||||||
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
||||||
contextMenu.saveFileAs.connect(saveFileAs)
|
contextMenu.saveFileAs.connect(saveFileAs)
|
||||||
contextMenu.reply.connect(function() {
|
contextMenu.reply.connect(function() {
|
||||||
roomPanelInput.replyModel = model
|
roomPanelInput.replyModel = Object.assign({}, model)
|
||||||
roomPanelInput.isReply = true
|
roomPanelInput.isReply = true
|
||||||
roomPanelInput.focus()
|
roomPanelInput.focus()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ ColumnLayout {
|
|||||||
padding: 0
|
padding: 0
|
||||||
|
|
||||||
background: AutoRectangle {
|
background: AutoRectangle {
|
||||||
readonly property int minorRadius: 2
|
readonly property int minorRadius: 8
|
||||||
|
|
||||||
id: bubbleBackground
|
id: bubbleBackground
|
||||||
|
|
||||||
@@ -100,7 +100,7 @@ ColumnLayout {
|
|||||||
messageSourceDialog.createObject(ApplicationWindow.overlay, {"sourceText": toolTip}).open()
|
messageSourceDialog.createObject(ApplicationWindow.overlay, {"sourceText": toolTip}).open()
|
||||||
})
|
})
|
||||||
contextMenu.reply.connect(function() {
|
contextMenu.reply.connect(function() {
|
||||||
roomPanelInput.replyModel = model
|
roomPanelInput.replyModel = Object.assign({}, model)
|
||||||
roomPanelInput.isReply = true
|
roomPanelInput.isReply = true
|
||||||
roomPanelInput.focus()
|
roomPanelInput.focus()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ RowLayout {
|
|||||||
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
contextMenu.downloadAndOpen.connect(downloadAndOpen)
|
||||||
contextMenu.saveFileAs.connect(saveFileAs)
|
contextMenu.saveFileAs.connect(saveFileAs)
|
||||||
contextMenu.reply.connect(function() {
|
contextMenu.reply.connect(function() {
|
||||||
roomPanelInput.replyModel = model
|
roomPanelInput.replyModel = Object.assign({}, model)
|
||||||
roomPanelInput.isReply = true
|
roomPanelInput.isReply = true
|
||||||
roomPanelInput.focus()
|
roomPanelInput.focus()
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ Control {
|
|||||||
property var replyModel
|
property var replyModel
|
||||||
readonly property var replyUser: replyModel ? replyModel.author : null
|
readonly property var replyUser: replyModel ? replyModel.author : null
|
||||||
readonly property string replyEventID: replyModel ? replyModel.eventId : ""
|
readonly property string replyEventID: replyModel ? replyModel.eventId : ""
|
||||||
readonly property string replyContent: replyModel ? replyModel.message : ""
|
readonly property string replyContent: replyModel ? replyModel.display : ""
|
||||||
|
|
||||||
property alias isAutoCompleting: autoCompleteListView.visible
|
property alias isAutoCompleting: autoCompleteListView.visible
|
||||||
property var autoCompleteModel
|
property var autoCompleteModel
|
||||||
@@ -62,13 +62,20 @@ Control {
|
|||||||
hint: replyUser ? replyUser.displayName : "No name"
|
hint: replyUser ? replyUser.displayName : "No name"
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
TextEdit {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
|
||||||
text: replyContent
|
color: MPalette.foreground
|
||||||
font.pixelSize: 14
|
text: "<style>a{color: " + color + ";} .user-pill{}</style>" + replyContent
|
||||||
|
|
||||||
|
font.family: window.font.family
|
||||||
|
font.pixelSize: 14
|
||||||
|
selectByMouse: true
|
||||||
|
readOnly: true
|
||||||
wrapMode: Label.Wrap
|
wrapMode: Label.Wrap
|
||||||
|
selectedTextColor: "white"
|
||||||
|
selectionColor: MPalette.accent
|
||||||
|
textFormat: Text.RichText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -285,12 +285,11 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const {
|
|||||||
const auto& evt = isPending ? **pendingIt : **timelineIt;
|
const auto& evt = isPending ? **pendingIt : **timelineIt;
|
||||||
|
|
||||||
if (role == Qt::DisplayRole) {
|
if (role == Qt::DisplayRole) {
|
||||||
return utils::cleanHTML(
|
return m_currentRoom->eventToString(evt, Qt::RichText);
|
||||||
utils::removeReply(m_currentRoom->eventToString(evt, Qt::RichText)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == MessageRole) {
|
if (role == MessageRole) {
|
||||||
return utils::removeReply(m_currentRoom->eventToString(evt));
|
return m_currentRoom->eventToString(evt);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (role == Qt::ToolTipRole) {
|
if (role == Qt::ToolTipRole) {
|
||||||
@@ -443,8 +442,7 @@ QVariant MessageEventModel::data(const QModelIndex& idx, int role) const {
|
|||||||
|
|
||||||
return QVariantMap{
|
return QVariantMap{
|
||||||
{"eventId", replyEventId},
|
{"eventId", replyEventId},
|
||||||
{"display", utils::cleanHTML(utils::removeReply(
|
{"display", m_currentRoom->eventToString(replyEvt, Qt::RichText)},
|
||||||
m_currentRoom->eventToString(replyEvt, Qt::RichText)))},
|
|
||||||
{"author",
|
{"author",
|
||||||
QVariant::fromValue(m_currentRoom->user(replyEvt.senderId()))}};
|
QVariant::fromValue(m_currentRoom->user(replyEvt.senderId()))}};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ QString SpectralRoom::lastEvent() const {
|
|||||||
|
|
||||||
return user(evt->senderId())->displayname() +
|
return user(evt->senderId())->displayname() +
|
||||||
(evt->isStateEvent() ? " " : ": ") +
|
(evt->isStateEvent() ? " " : ": ") +
|
||||||
utils::removeReply(eventToString(*evt));
|
eventToString(*evt);
|
||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@@ -230,18 +230,27 @@ QString SpectralRoom::avatarMediaId() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString SpectralRoom::eventToString(const RoomEvent& evt,
|
QString SpectralRoom::eventToString(const RoomEvent& evt,
|
||||||
Qt::TextFormat format) const {
|
Qt::TextFormat format, bool removeReply) const {
|
||||||
const bool prettyPrint = (format == Qt::RichText);
|
const bool prettyPrint = (format == Qt::RichText);
|
||||||
|
|
||||||
using namespace QMatrixClient;
|
using namespace QMatrixClient;
|
||||||
return visit(
|
return visit(
|
||||||
evt,
|
evt,
|
||||||
[prettyPrint](const RoomMessageEvent& e) {
|
[prettyPrint, removeReply](const RoomMessageEvent& e) {
|
||||||
using namespace MessageEventContent;
|
using namespace MessageEventContent;
|
||||||
|
|
||||||
if (prettyPrint && e.hasTextContent() &&
|
if (prettyPrint && e.hasTextContent() &&
|
||||||
e.mimeType().name() != "text/plain")
|
e.mimeType().name() != "text/plain") {
|
||||||
return static_cast<const TextContent*>(e.content())->body;
|
auto htmlBody = static_cast<const TextContent*>(e.content())->body;
|
||||||
|
if (removeReply) {
|
||||||
|
htmlBody.remove(utils::removeRichReplyRegex);
|
||||||
|
}
|
||||||
|
htmlBody.replace(utils::userPillRegExp, "<b>\\1</b>");
|
||||||
|
htmlBody.replace(utils::strikethroughRegExp, "<s>\\1</s>");
|
||||||
|
htmlBody.push_front("<style>pre {white-space: pre-wrap}</style>");
|
||||||
|
return htmlBody;
|
||||||
|
}
|
||||||
|
|
||||||
if (e.hasFileContent()) {
|
if (e.hasFileContent()) {
|
||||||
auto fileCaption =
|
auto fileCaption =
|
||||||
e.content()->fileInfo()->originalName.toHtmlEscaped();
|
e.content()->fileInfo()->originalName.toHtmlEscaped();
|
||||||
@@ -252,8 +261,18 @@ QString SpectralRoom::eventToString(const RoomEvent& evt,
|
|||||||
}
|
}
|
||||||
return !fileCaption.isEmpty() ? fileCaption : tr("a file");
|
return !fileCaption.isEmpty() ? fileCaption : tr("a file");
|
||||||
}
|
}
|
||||||
return prettyPrint ? QMatrixClient::prettyPrint(e.plainBody())
|
|
||||||
: e.plainBody();
|
if (prettyPrint) {
|
||||||
|
auto plainBody = e.plainBody();
|
||||||
|
if (removeReply) {
|
||||||
|
return plainBody.remove(utils::removeReplyRegex);
|
||||||
|
}
|
||||||
|
return QMatrixClient::prettyPrint(plainBody);
|
||||||
|
}
|
||||||
|
if (removeReply) {
|
||||||
|
return e.plainBody().remove(utils::removeReplyRegex);
|
||||||
|
}
|
||||||
|
return e.plainBody();
|
||||||
},
|
},
|
||||||
[this](const RoomMemberEvent& e) {
|
[this](const RoomMemberEvent& e) {
|
||||||
// FIXME: Rewind to the name that was at the time of this event
|
// FIXME: Rewind to the name that was at the time of this event
|
||||||
@@ -478,7 +497,7 @@ void SpectralRoom::postPlainMessage(const QString& text,
|
|||||||
replyEventId +
|
replyEventId +
|
||||||
"\">In reply to</a> <a href=\"https://matrix.to/#/" +
|
"\">In reply to</a> <a href=\"https://matrix.to/#/" +
|
||||||
replyEvt.senderId() + "\">" + replyEvt.senderId() + "</a><br>" +
|
replyEvt.senderId() + "\">" + replyEvt.senderId() + "</a><br>" +
|
||||||
utils::removeReply(eventToString(replyEvt, Qt::RichText)) +
|
eventToString(replyEvt, Qt::RichText) +
|
||||||
"</blockquote></mx-reply>" + text.toHtmlEscaped()}};
|
"</blockquote></mx-reply>" + text.toHtmlEscaped()}};
|
||||||
postJson("m.room.message", json);
|
postJson("m.room.message", json);
|
||||||
|
|
||||||
@@ -513,7 +532,7 @@ void SpectralRoom::postHtmlMessage(const QString& text,
|
|||||||
replyEventId +
|
replyEventId +
|
||||||
"\">In reply to</a> <a href=\"https://matrix.to/#/" +
|
"\">In reply to</a> <a href=\"https://matrix.to/#/" +
|
||||||
replyEvt.senderId() + "\">" + replyEvt.senderId() + "</a><br>" +
|
replyEvt.senderId() + "\">" + replyEvt.senderId() + "</a><br>" +
|
||||||
utils::removeReply(eventToString(replyEvt, Qt::RichText)) +
|
eventToString(replyEvt, Qt::RichText) +
|
||||||
"</blockquote></mx-reply>" + html}};
|
"</blockquote></mx-reply>" + html}};
|
||||||
postJson("m.room.message", json);
|
postJson("m.room.message", json);
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ class SpectralRoom : public Room {
|
|||||||
QString avatarMediaId() const;
|
QString avatarMediaId() const;
|
||||||
|
|
||||||
QString eventToString(const RoomEvent& evt,
|
QString eventToString(const RoomEvent& evt,
|
||||||
Qt::TextFormat format = Qt::PlainText) const;
|
Qt::TextFormat format = Qt::PlainText, bool removeReply = true) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_cachedInput;
|
QString m_cachedInput;
|
||||||
|
|||||||
@@ -1,15 +1 @@
|
|||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
QString utils::removeReply(const QString& text) {
|
|
||||||
QString result(text);
|
|
||||||
result.remove(utils::removeRichReplyRegex);
|
|
||||||
result.remove(utils::removeReplyRegex);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString utils::cleanHTML(const QString& text) {
|
|
||||||
QString result(text);
|
|
||||||
result.replace(codePillRegExp, "<i>\\1</i>");
|
|
||||||
result.replace(userPillRegExp, "<b>\\1</b>");
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,13 +19,12 @@ static const QRegularExpression removeReplyRegex{
|
|||||||
static const QRegularExpression removeRichReplyRegex{
|
static const QRegularExpression removeRichReplyRegex{
|
||||||
"<mx-reply>.*?</mx-reply>", QRegularExpression::DotMatchesEverythingOption};
|
"<mx-reply>.*?</mx-reply>", QRegularExpression::DotMatchesEverythingOption};
|
||||||
static const QRegularExpression codePillRegExp{
|
static const QRegularExpression codePillRegExp{
|
||||||
"<pre>(.*?)</pre>", QRegularExpression::DotMatchesEverythingOption};
|
"<pre><code[^>]*>(.*?)</code></pre>", QRegularExpression::DotMatchesEverythingOption};
|
||||||
static const QRegularExpression userPillRegExp{
|
static const QRegularExpression userPillRegExp{
|
||||||
"<a href=\"https://matrix.to/#/@.*?:.*?\">(.*?)</a>",
|
"<a href=\"https://matrix.to/#/@.*?:.*?\">(.*?)</a>",
|
||||||
QRegularExpression::DotMatchesEverythingOption};
|
QRegularExpression::DotMatchesEverythingOption};
|
||||||
|
static const QRegularExpression strikethroughRegExp{
|
||||||
QString removeReply(const QString& text);
|
"<del>(.*?)</del>", QRegularExpression::DotMatchesEverythingOption};
|
||||||
QString cleanHTML(const QString& text);
|
|
||||||
} // namespace utils
|
} // namespace utils
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user