mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-25 20:48:52 +03:00
Merge pull request #535 from LordMZTE/feature/rainbow
add /rainbow command
This commit is contained in:
commit
326f48d87f
4 changed files with 95 additions and 8 deletions
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in a new issue