Merge pull request #535 from LordMZTE/feature/rainbow

add /rainbow command
This commit is contained in:
DeepBlueV7.X 2021-03-28 18:23:50 +00:00 committed by GitHub
commit 326f48d87f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 95 additions and 8 deletions

View file

@ -17,6 +17,7 @@
#include <QTextDocument> #include <QTextDocument>
#include <QXmlStreamReader> #include <QXmlStreamReader>
#include <QTextBoundaryFinder>
#include <cmath> #include <cmath>
#include <variant> #include <variant>
@ -468,11 +469,92 @@ utils::escapeBlacklistedHtml(const QString &rawStr)
} }
QString QString
utils::markdownToHtml(const QString &text) utils::markdownToHtml(const QString &text, bool rainbowify)
{ {
const auto str = text.toUtf8(); const auto str = text.toUtf8();
const char *tmp_buf = cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_UNSAFE); cmark_node *const node =
cmark_parse_document(str.constData(), str.size(), CMARK_OPT_UNSAFE);
if (rainbowify) {
// create iterator over node
cmark_iter *iter = cmark_iter_new(node);
cmark_event_type ev_type;
// First loop to get total text length
int textLen = 0;
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
cmark_node *cur = cmark_iter_get_node(iter);
// only text nodes (no code or semilar)
if (cmark_node_get_type(cur) != CMARK_NODE_TEXT)
continue;
// count up by length of current node's text
QTextBoundaryFinder tbf(QTextBoundaryFinder::BoundaryType::Grapheme,
QString(cmark_node_get_literal(cur)));
while (tbf.toNextBoundary() != -1)
textLen++;
}
// create new iter to start over
cmark_iter_free(iter);
iter = cmark_iter_new(node);
// Second loop to rainbowify
int charIdx = 0;
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
cmark_node *cur = cmark_iter_get_node(iter);
// only text nodes (no code or semilar)
if (cmark_node_get_type(cur) != CMARK_NODE_TEXT)
continue;
// get text in current node
QString nodeText(cmark_node_get_literal(cur));
// create buffer to append rainbow text to
QString buf;
int boundaryStart = 0;
int boundaryEnd = 0;
// use QTextBoundaryFinder to iterate ofer graphemes
QTextBoundaryFinder tbf(QTextBoundaryFinder::BoundaryType::Grapheme,
nodeText);
while ((boundaryEnd = tbf.toNextBoundary()) != -1) {
// Split text to get current char
auto curChar =
nodeText.midRef(boundaryStart, boundaryEnd - boundaryStart);
boundaryStart = boundaryEnd;
// Don't rainbowify whitespaces
if (curChar.trimmed().isEmpty()) {
buf.append(curChar.toString());
continue;
}
// get correct color for char index
auto color = QColor::fromHsvF(1.0 / textLen * charIdx, 1.0, 1.0);
// format color for HTML
auto colorString = color.name(QColor::NameFormat::HexRgb);
// create HTML element for current char
auto curCharColored = QString("<font color=\"%0\">%1</font>")
.arg(colorString)
.arg(curChar);
// append colored HTML element to buffer
buf.append(curCharColored);
charIdx++;
}
// create HTML_INLINE node to prevent HTML from being escaped
auto htmlNode = cmark_node_new(CMARK_NODE_HTML_INLINE);
// set content of HTML node to buffer contents
cmark_node_set_literal(htmlNode, buf.toUtf8().data());
// replace current node with HTML node
cmark_node_replace(cur, htmlNode);
// free memory of old node
cmark_node_free(cur);
}
cmark_iter_free(iter);
}
const char *tmp_buf = cmark_render_html(node, CMARK_OPT_UNSAFE);
// Copy the null terminated output buffer. // Copy the null terminated output buffer.
std::string html(tmp_buf); std::string html(tmp_buf);

View file

@ -268,7 +268,7 @@ linkifyMessage(const QString &body);
//! Convert the input markdown text to html. //! Convert the input markdown text to html.
QString QString
markdownToHtml(const QString &text); markdownToHtml(const QString &text, bool rainbowify = false);
//! Escape every html tag, that was not whitelisted //! Escape every html tag, that was not whitelisted
QString QString

View file

@ -13,6 +13,7 @@
#include <QStandardPaths> #include <QStandardPaths>
#include <QUrl> #include <QUrl>
#include <QRegularExpression>
#include <mtx/responses/common.hpp> #include <mtx/responses/common.hpp>
#include <mtx/responses/media.hpp> #include <mtx/responses/media.hpp>
@ -203,7 +204,7 @@ InputBar::send()
auto wasEdit = !room->edit().isEmpty(); auto wasEdit = !room->edit().isEmpty();
if (text().startsWith('/')) { if (text().startsWith('/')) {
int command_end = text().indexOf(' '); int command_end = text().indexOf(QRegularExpression("\\s"));
if (command_end == -1) if (command_end == -1)
command_end = text().size(); command_end = text().size();
auto name = text().mid(1, command_end - 1); auto name = text().mid(1, command_end - 1);
@ -255,7 +256,7 @@ InputBar::openFileSelection()
} }
void void
InputBar::message(QString msg, MarkdownOverride useMarkdown) InputBar::message(QString msg, MarkdownOverride useMarkdown, bool rainbowify)
{ {
mtx::events::msg::Text text = {}; mtx::events::msg::Text text = {};
text.body = msg.trimmed().toStdString(); text.body = msg.trimmed().toStdString();
@ -263,7 +264,7 @@ InputBar::message(QString msg, MarkdownOverride useMarkdown)
if ((ChatPage::instance()->userSettings()->markdown() && if ((ChatPage::instance()->userSettings()->markdown() &&
useMarkdown == MarkdownOverride::NOT_SPECIFIED) || useMarkdown == MarkdownOverride::NOT_SPECIFIED) ||
useMarkdown == MarkdownOverride::ON) { useMarkdown == MarkdownOverride::ON) {
text.formatted_body = utils::markdownToHtml(msg).toStdString(); text.formatted_body = utils::markdownToHtml(msg, rainbowify).toStdString();
// Remove markdown links by completer // Remove markdown links by completer
text.body = text.body =
msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString(); msg.trimmed().replace(conf::strings::matrixToMarkdownLink, "\\1").toStdString();
@ -526,6 +527,8 @@ InputBar::command(QString command, QString args)
message(args, MarkdownOverride::ON); message(args, MarkdownOverride::ON);
} else if (command == "plain") { } else if (command == "plain") {
message(args, MarkdownOverride::OFF); message(args, MarkdownOverride::OFF);
} else if (command == "rainbow") {
message(args, MarkdownOverride::ON, true);
} }
} }

View file

@ -53,7 +53,9 @@ public slots:
void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text); void updateState(int selectionStart, int selectionEnd, int cursorPosition, QString text);
void openFileSelection(); void openFileSelection();
bool uploading() const { return uploading_; } bool uploading() const { return uploading_; }
void message(QString body, MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED); void message(QString body,
MarkdownOverride useMarkdown = MarkdownOverride::NOT_SPECIFIED,
bool rainbowify = false);
private slots: private slots:
void startTyping(); void startTyping();