diff options
Diffstat (limited to 'src/htmlbuilder.cpp')
| -rw-r--r-- | src/htmlbuilder.cpp | 103 |
1 files changed, 91 insertions, 12 deletions
diff --git a/src/htmlbuilder.cpp b/src/htmlbuilder.cpp index ccb9171..c8f039f 100644 --- a/src/htmlbuilder.cpp +++ b/src/htmlbuilder.cpp @@ -18,25 +18,92 @@ #include "htmlbuilder.h" +#include <QCoreApplication> +#include <QGuiApplication> #include <QRegularExpression> namespace { -const char *kStyle = R"CSS( -body { font-family: sans-serif; font-size: 10pt; margin: 12px; } +/// The stylesheet, with every colour supplied by the caller. +/// +/// %1 background, %2 text, %3 quote, %4 border, %5 dim. +const char *kStyleTemplate = R"CSS( +body { font-family: sans-serif; font-size: 10pt; margin: 12px; + background: %1; color: %2; } pre.plain { white-space: pre-wrap; word-wrap: break-word; font-family: monospace; margin: 0; } -span.quote { color: #4a6f8a; } -.message { border-top: 1px solid #bbb; padding: 10px 0; } +span.quote { color: %3; } +.message { border-top: 1px solid %4; padding: 10px 0; } .message:first-child { border-top: none; } -.msg-header { font-size: 9pt; color: #555; margin-bottom: 8px; } -.msg-header .who { font-weight: bold; color: #000; } -.stub { font-size: 9pt; color: #666; padding: 4px 0; - border-top: 1px solid #ddd; } +.msg-header { font-size: 9pt; color: %5; margin-bottom: 8px; } +.msg-header .who { font-weight: bold; color: %2; } +.stub { font-size: 9pt; color: %5; padding: 4px 0; + border-top: 1px solid %4; } )CSS"; +/// Mixes two colours, `weight` being how much of `a` survives. +/// +/// Blending is what makes the derived colours theme-correct. A fixed grey is +/// only "subtle" against the background it was chosen for: #555 reads as a +/// quiet label on white and nearly vanishes on near-black. +QColor blend(const QColor &a, const QColor &b, qreal weight) +{ + const qreal inverse = 1.0 - weight; + return QColor::fromRgbF(a.redF() * weight + b.redF() * inverse, + a.greenF() * weight + b.greenF() * inverse, + a.blueF() * weight + b.blueF() * inverse); +} + } // namespace +HtmlBuilder::Palette HtmlBuilder::paletteFrom(const QPalette &palette) +{ + // Base and Text, not Window and WindowText: the pane is a content surface + // like a text edit, and on many themes Base differs from Window. + const QColor background = palette.color(QPalette::Base); + const QColor text = palette.color(QPalette::Text); + + Palette result; + result.background = background; + result.text = text; + + // Both derived from the pair, so they land at the right contrast whichever + // way round the theme is. + result.dim = blend(text, background, 0.6); + result.border = blend(text, background, 0.25); + + // The quote colour keeps its hue, since "this is quoted" is carried by the + // colour being different rather than by it being dimmer, but it is pulled + // toward the background so it stays readable on a dark theme instead of + // glowing. + const QColor quoteHue(0x4a, 0x6f, 0x8a); + result.quote = background.lightnessF() < 0.5 + ? blend(quoteHue.lighter(160), background, 0.75) + : blend(quoteHue, background, 0.85); + + return result; +} + +HtmlBuilder::Palette HtmlBuilder::defaultPalette() +{ + if (const QGuiApplication *app = + qobject_cast<QGuiApplication *>(QCoreApplication::instance())) + return paletteFrom(app->palette()); + + // No GUI application: only reachable from a test that did not pass a + // palette. A default-constructed QPalette is light, which matches what + // this code did before it was themed at all. + return paletteFrom(QPalette()); +} + +QString HtmlBuilder::styleSheet(const Palette &palette) +{ + return QString::fromUtf8(kStyleTemplate) + .arg(palette.background.name(), palette.text.name(), + palette.quote.name(), palette.border.name(), + palette.dim.name()); +} + QString HtmlBuilder::renderPlain(const QString &text) { QString out; @@ -61,12 +128,12 @@ QString HtmlBuilder::renderPlain(const QString &text) return out; } -QString HtmlBuilder::document(const QString &bodyHtml) +QString HtmlBuilder::document(const QString &bodyHtml, const Palette &palette) { return QStringLiteral( "<!DOCTYPE html><html><head><meta charset=\"utf-8\">" "<style>%1</style></head><body>%2</body></html>") - .arg(QString::fromUtf8(kStyle), bodyHtml); + .arg(styleSheet(palette), bodyHtml); } QString HtmlBuilder::namespaceCids(const QString &html, const QString &prefix) @@ -218,14 +285,26 @@ QString HtmlBuilder::renderStub(const ParsedMessage &message) QString HtmlBuilder::build(const ParsedMessage &message, Mode mode) { + return build(message, mode, defaultPalette()); +} + +QString HtmlBuilder::build(const ParsedMessage &message, Mode mode, + const Palette &palette) +{ ThreadRenderItem item; item.message = message; item.expanded = true; - return document(renderBody(item, mode)); + return document(renderBody(item, mode), palette); } QString HtmlBuilder::buildThread(const QList<ThreadRenderItem> &items, Mode mode) { + return buildThread(items, mode, defaultPalette()); +} + +QString HtmlBuilder::buildThread(const QList<ThreadRenderItem> &items, Mode mode, + const Palette &palette) +{ QString body; for (int i = 0; i < items.size(); ++i) { @@ -246,5 +325,5 @@ QString HtmlBuilder::buildThread(const QList<ThreadRenderItem> &items, Mode mode renderBody(item, mode)); } - return document(body); + return document(body, palette); } |
