aboutsummaryrefslogtreecommitdiff
path: root/src/renderers/geminirenderer.cpp
diff options
context:
space:
mode:
authorMike Skec <skec@protonmail.ch>2021-02-20 20:05:43 +1100
committerFelix Queißner <felix@ib-queissner.de>2021-02-20 12:10:48 +0100
commitb03693711ab79f22da85998924145f1436b627aa (patch)
treee53c17feae674da446550c6ade04d540cbbf7f14 /src/renderers/geminirenderer.cpp
parent995ff2c30ac9e062c698844ddfa7fc06a941197e (diff)
downloadkristall-b03693711ab79f22da85998924145f1436b627aa.tar.gz
Fix experimental text highlights
The that was used to achieve this is a bit messy/crazy, however, it seems to work quite well for all the cases I tested it on. This will finally close #141
Diffstat (limited to 'src/renderers/geminirenderer.cpp')
-rw-r--r--src/renderers/geminirenderer.cpp175
1 files changed, 133 insertions, 42 deletions
diff --git a/src/renderers/geminirenderer.cpp b/src/renderers/geminirenderer.cpp
index 595db44..095d482 100644
--- a/src/renderers/geminirenderer.cpp
+++ b/src/renderers/geminirenderer.cpp
@@ -7,6 +7,7 @@
#include <QStringList>
#include <QDebug>
#include <QTextTable>
+#include <QRegularExpression>
#include "kristall.hpp"
@@ -258,64 +259,154 @@ std::unique_ptr<GeminiDocument> GeminiRenderer::render(
if(emit_fancy_text)
{
- // TODO: Fix UTF-8 encoding here… Don't emit single characters but always spans!
+ // Just render lines not containing asterisks/underscores normally.
+ // This actually helps reduce the small overhead on large pages to
+ // being almost negligable
+ if (!line.contains("*") && !line.contains("_"))
+ {
+ cursor.insertText(line + "\n", text_style.standard);
+ continue;
+ }
- bool rendering_bold = false;
- bool rendering_underlined = false;
+ // Easier to work on this as an array of QChars
+ QString text(line);
- QTextCharFormat fmt = text_style.standard;
+ // Whether to hide formatting codes (*, and _). This option
+ // is mainly here so that the code which strips these is
+ // more understandable.
+ static const bool HIDE_FORMATS = true;
- QByteArray buffer;
+ // The first thing we do is convert double-asterisk bolding to single-asterisk.
+ // This makes it A LOT easier to bold these things.
+ //
+ // This is done using this regex. In a simpler, pseudo form, it can be written as:
+ // (punctuation/whitespace/line-begin)+\*\*(bolded text)\*\*(punctuation/whitespace/EOL)
+ // Just stare at it a bit and you might figure out how it works...
+ QRegularExpression BOLD_DBL_REGEX
+ = QRegularExpression(R"((^|[\s.,!?[\]()\\-])+\*\*([^\*\s]+[^\*]+[^\*\s]+)\*\*($|[\s.,!?[\]()\\-]))");
+ text.replace(BOLD_DBL_REGEX, QString(R"(\1*\2*\3)"));
+
+ QTextCharFormat fmt = text_style.standard;
+ bool bold = false, underline = false;
+ bool was_bold = false, was_underline = false;
+ int last = 0;
- auto flush = [&]() {
- if(buffer.size() > 0) {
- cursor.insertText(QString::fromUtf8(buffer), fmt);
- buffer.resize(0);
+ // Used to prepare the format before actually drawing the text.
+ auto format_text = [&bold, &underline, &was_bold, &was_underline, &last, &text, &fmt](int i) -> QString
+ {
+ // Makes sure that bold/underline text only gets printed
+ // if it has a matching * or _.
+ if (bold && !text.mid(i, text.length() - i).contains("*"))
+ bold = false;
+ if (underline && !text.mid(i, text.length() - i).contains("_"))
+ underline = false;
+
+ // Sets format to bold/underline as necessary.
+ auto f = fmt.font();
+ f.setBold(bold);
+ fmt.setFont(f);
+ fmt.setUnderlineStyle(underline ?
+ QTextCharFormat::SingleUnderline : QTextCharFormat::NoUnderline);
+
+ // Remove formats
+ QString span = text.mid(last, i - last);
+ if (HIDE_FORMATS &&
+ span.length() > 1 &&
+ (((bold || was_bold) && span.startsWith("*")) ||
+ ((underline || was_underline) && span.startsWith("_"))))
+ {
+ span = span.mid(1, span.length() - 1);
}
+
+ return span;
};
- for(int i = 0; i < line.length(); i += 1)
+ for (int i = 0; i < text.length(); ++i)
{
- char c = line.at(i);
- if(c == ' ') {
- flush();
- fmt = text_style.standard;
- buffer.append(' ');
- rendering_bold = false;
- rendering_underlined = false;
- }
- else if(c == '*') {
- if(rendering_bold) {
- buffer.append('*');
+ if (text[i] == '*')
+ {
+ // Format and insert the text.
+ cursor.insertText(format_text(i), fmt);
+
+ // 'Toggle' bold state.
+ if (was_bold) was_bold = false;
+ if (bold) {
+ was_bold = true;
+ bold = false;
+ } else {
+ // Only start bold formatting if this looks like bold formatting:
+ // * Previous char must be either whitespace, nothing
+ // * Next char must not be: whitespace, comma, full-stop, asterisk, or underscore.
+ if ((i == 0 || text[i - 1].isSpace()) &&
+ (i + 1) < text.length() &&
+ !text[i + 1].isSpace() &&
+ text[i + 1] != ',' &&
+ text[i + 1] != '.' &&
+ text[i + 1] != '*' &&
+ text[i + 1] != '_')
+ {
+ bold = true;
+ }
}
- flush();
- rendering_bold = not rendering_bold;
- auto f = fmt.font();
- f.setBold(rendering_bold);
- fmt.setFont(f);
- if(rendering_bold) {
- buffer.append('*');
+
+ last = i;
+ }
+ else if (text[i] == '_')
+ {
+ // Insert the text
+ cursor.insertText(format_text(i), fmt);
+
+ // 'Toggle' underline state.
+ if (was_underline) was_underline = false;
+ if (underline) {
+ was_underline = true;
+ underline = false;
+ } else {
+ // Only start underline formatting if it looks like an underline.
+ // * Previous char must be either whitespace or nothing
+ // * Next char must not be: whitespace, comma, full-stop, asterisk, or underscore.
+ if ((i == 0 || text[i - 1].isSpace()) &&
+ (i + 1) < text.length() &&
+ !text[i + 1].isSpace() &&
+ text[i + 1] != ',' &&
+ text[i + 1] != '.' &&
+ text[i + 1] != '*' &&
+ text[i + 1] != '_')
+ {
+ underline = true;
+ }
}
+
+ last = i;
}
- else if(c == '_') {
- if(rendering_underlined) {
- buffer.append(' ');
+
+ if (i == text.length() - 1)
+ {
+ QString span = text.mid(last, i - last + 1);
+
+ // Skip if the span is just an asterisk/underline
+ if (HIDE_FORMATS &&
+ ((was_bold && span == "*") ||
+ (was_underline && span == "_")))
+ {
+ break;
}
- flush();
- rendering_underlined = not rendering_underlined;
- auto f = fmt.font();
- fmt.setUnderlineStyle(rendering_underlined ? QTextCharFormat::SingleUnderline : QTextCharFormat::NoUnderline);
- if(rendering_underlined) {
- buffer.append(' ');
+
+ // Strips previous underline/asterisk
+ if (HIDE_FORMATS &&
+ span.length() > 1 &&
+ ((was_bold && span.startsWith("*")) ||
+ (was_underline && span.startsWith("_"))))
+ {
+ span = span.mid(1, span.length() - 1);
}
- }
- else {
- buffer.append(c);
+
+ // Draw ending text normally.
+ cursor.insertText(span, text_style.standard);
+ break;
}
}
- flush();
-
cursor.insertText("\n", text_style.standard);
}
else {