aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFelix (xq) Queißner <git@mq32.de>2020-06-07 10:46:23 +0200
committerFelix (xq) Queißner <git@mq32.de>2020-06-07 10:46:23 +0200
commit425f9d41cd337133d5677744eef937a8a2a61212 (patch)
tree6cd5c2603e1499b89aae4fe5e56c6e650cb2117e /src
parentec95bb371e54116a2627c162eac3357ec13f06ad (diff)
downloadkristall-425f9d41cd337133d5677744eef937a8a2a61212.tar.gz
Adds support for light/dark widget theme, adds experiemental support for http style sheets.
Diffstat (limited to 'src')
-rw-r--r--src/browsertab.cpp23
-rw-r--r--src/documentoutlinemodel.cpp3
-rw-r--r--src/geminirenderer.cpp127
-rw-r--r--src/geminirenderer.hpp20
-rw-r--r--src/kristall.pro1
-rw-r--r--src/main.cpp9
-rw-r--r--src/mainwindow.cpp34
-rw-r--r--src/mainwindow.hpp6
-rw-r--r--src/settingsdialog.cpp29
-rw-r--r--src/settingsdialog.hpp3
-rw-r--r--src/settingsdialog.ui18
11 files changed, 242 insertions, 31 deletions
diff --git a/src/browsertab.cpp b/src/browsertab.cpp
index 8dcbe64..2a24815 100644
--- a/src/browsertab.cpp
+++ b/src/browsertab.cpp
@@ -199,29 +199,40 @@ void BrowserTab::on_requestComplete(const QByteArray &data, const QString &mime)
this->outline.clear();
+ auto doc_style = mainWindow->current_style.derive(this->current_location);
+
+ this->ui->text_browser->setStyleSheet(QString("QTextBrowser { background-color: %1; }").arg(doc_style.background_color.name()));
+
if(mime.startsWith("text/gemini")) {
- auto doc= GeminiRenderer{ mainWindow->current_style }.render(data, this->current_location, this->outline);
- this->ui->text_browser->setStyleSheet(QString("QTextBrowser { background-color: %1; }").arg(doc->background_color.name()));
+ auto doc= GeminiRenderer::render(
+ data,
+ this->current_location,
+ doc_style,
+ this->outline);
document = std::move(doc);
}
else if(mime.startsWith("text/html")) {
document = std::make_unique<QTextDocument>();
+
+ document->setDefaultFont(doc_style.standard_font);
+ document->setDefaultStyleSheet(doc_style.toStyleSheet());
document->setHtml(QString::fromUtf8(data));
}
#if defined(QT_FEATURE_textmarkdownreader)
else if(mime.startsWith("text/markdown")) {
document = std::make_unique<QTextDocument>();
+ document->setDefaultFont(doc_style.standard_font);
+ document->setDefaultStyleSheet(doc_style.toStyleSheet());
+
document->setMarkdown(QString::fromUtf8(data));
}
#endif
else if(mime.startsWith("text/")) {
- QFont monospace;
- monospace.setFamily("monospace");
-
document = std::make_unique<QTextDocument>();
- document->setDefaultFont(monospace);
+ document->setDefaultFont(doc_style.standard_font);
+ document->setDefaultStyleSheet(doc_style.toStyleSheet());
document->setPlainText(QString::fromUtf8(data));
}
else if(mime.startsWith("image/")) {
diff --git a/src/documentoutlinemodel.cpp b/src/documentoutlinemodel.cpp
index 5978f33..e98f072 100644
--- a/src/documentoutlinemodel.cpp
+++ b/src/documentoutlinemodel.cpp
@@ -151,6 +151,7 @@ int DocumentOutlineModel::rowCount(const QModelIndex &parent) const
int DocumentOutlineModel::columnCount(const QModelIndex &parent) const
{
+ Q_UNUSED(parent)
return 1;
}
@@ -188,7 +189,7 @@ DocumentOutlineModel::Node & DocumentOutlineModel::ensureLevel2()
auto & parent = ensureLevel1();
if(parent.children.size() == 0) {
- root.children.append(Node {
+ parent.children.append(Node {
&parent,
"<missing layer>", "",
2, 0,
diff --git a/src/geminirenderer.cpp b/src/geminirenderer.cpp
index 811a946..ad6a2fc 100644
--- a/src/geminirenderer.cpp
+++ b/src/geminirenderer.cpp
@@ -5,6 +5,112 @@
#include <QTextBlock>
#include <QDebug>
#include <cmath>
+#include <QList>
+#include <QStringList>
+
+static QString encodeCssFont (const QFont& refFont)
+{
+ //-----------------------------------------------------------------------
+ // This function assembles a CSS Font specification string from
+ // a QFont. This supports most of the QFont attributes settable in
+ // the Qt 4.8 and Qt 5.3 QFontDialog.
+ //
+ // (1) Font Family
+ // (2) Font Weight (just bold or not)
+ // (3) Font Style (possibly Italic or Oblique)
+ // (4) Font Size (in either pixels or points)
+ // (5) Decorations (possibly Underline or Strikeout)
+ //
+ // Not supported: Writing System (e.g. Latin).
+ //
+ // See the corresponding decode function, below.
+ // QFont decodeCssFontString (const QString cssFontStr)
+ //-----------------------------------------------------------------------
+
+ QStringList fields; // CSS font attribute fields
+
+ // ***************************************************
+ // *** (1) Font Family: Primary plus Substitutes ***
+ // ***************************************************
+
+ const QString family = refFont.family();
+
+ // NOTE [9-2014, Qt 4.8.6]: This isn't what I thought it was. It
+ // does not return a list of "fallback" font faces (e.g. Georgia,
+ // Serif for "Times New Roman"). In my testing, this is always
+ // returning an empty list.
+ //
+ QStringList famSubs = QFont::substitutes (family);
+
+ if (!famSubs.contains (family))
+ famSubs.prepend (family);
+
+ static const QChar DBL_QUOT ('"');
+ const int famCnt = famSubs.count();
+ QStringList famList;
+ for (int inx = 0; inx < famCnt; ++inx)
+ {
+ // Place double quotes around family names having space characters,
+ // but only if double quotes are not already there.
+ //
+ const QString fam = famSubs [inx];
+ if (fam.contains (' ') && !fam.startsWith (DBL_QUOT))
+ famList << (DBL_QUOT + fam + DBL_QUOT);
+ else
+ famList << fam;
+ }
+
+ const QString famStr = QString ("font-family: ") + famList.join (", ");
+ fields << famStr;
+
+ // **************************************
+ // *** (2) Font Weight: Bold or Not ***
+ // **************************************
+
+ const bool bold = refFont.bold();
+ if (bold)
+ fields << "font-weight: bold";
+
+ // ****************************************************
+ // *** (3) Font Style: possibly Italic or Oblique ***
+ // ****************************************************
+
+ const QFont::Style style = refFont.style();
+ switch (style)
+ {
+ case QFont::StyleNormal: break;
+ case QFont::StyleItalic: fields << "font-style: italic"; break;
+ case QFont::StyleOblique: fields << "font-style: oblique"; break;
+ }
+
+ // ************************************************
+ // *** (4) Font Size: either Pixels or Points ***
+ // ************************************************
+
+ const double sizeInPoints = refFont.pointSizeF(); // <= 0 if not defined.
+ const int sizeInPixels = refFont.pixelSize(); // <= 0 if not defined.
+ if (sizeInPoints > 0.0)
+ fields << QString ("font-size: %1pt") .arg (sizeInPoints);
+ else if (sizeInPixels > 0)
+ fields << QString ("font-size: %1px") .arg (sizeInPixels);
+
+ // ***********************************************
+ // *** (5) Decorations: Underline, Strikeout ***
+ // ***********************************************
+
+ const bool underline = refFont.underline();
+ const bool strikeOut = refFont.strikeOut();
+
+ if (underline && strikeOut)
+ fields << "text-decoration: underline line-through";
+ else if (underline)
+ fields << "text-decoration: underline";
+ else if (strikeOut)
+ fields << "text-decoration: line-through";
+
+ const QString cssFontStr = fields.join ("; ");
+ return cssFontStr;
+}
static QByteArray trim_whitespace(QByteArray items)
{
@@ -182,14 +288,27 @@ GeminiStyle GeminiStyle::derive(const QUrl &url) const
return themed;
}
-GeminiRenderer::GeminiRenderer(GeminiStyle const &_style) : style(_style)
+QString GeminiStyle::toStyleSheet() const
{
+ QString css;
+
+ css += QString("p { color: %2; %1 }\n").arg(encodeCssFont (standard_font)).arg(standard_color.name());
+ css += QString("a { color: %2; %1 }\n").arg(encodeCssFont (standard_font)).arg(external_link_color.name());
+ css += QString("pre { color: %2; %1 }\n").arg(encodeCssFont (preformatted_font)).arg(preformatted_color.name());
+ css += QString("h1 { color: %2; %1 }\n").arg(encodeCssFont (h1_font)).arg(h1_color.name());
+ css += QString("h2 { color: %2; %1 }\n").arg(encodeCssFont (h2_font)).arg(h2_color.name());
+ css += QString("h3 { color: %2; %1 }\n").arg(encodeCssFont (h3_font)).arg(h3_color.name());
+
+ qDebug() << "CSS → " << css;
+ return css;
}
-std::unique_ptr<GeminiDocument> GeminiRenderer::render(const QByteArray &input, QUrl const &root_url, DocumentOutlineModel &outline)
+std::unique_ptr<GeminiDocument> GeminiRenderer::render(
+ const QByteArray &input,
+ QUrl const &root_url,
+ GeminiStyle const & themed_style,
+ DocumentOutlineModel &outline)
{
- auto themed_style = style.derive(root_url);
-
QTextCharFormat preformatted;
preformatted.setFont(themed_style.preformatted_font);
preformatted.setForeground(themed_style.preformatted_color);
diff --git a/src/geminirenderer.hpp b/src/geminirenderer.hpp
index 2ec1651..c93fd4e 100644
--- a/src/geminirenderer.hpp
+++ b/src/geminirenderer.hpp
@@ -47,6 +47,10 @@ struct GeminiStyle
//! Create a new style with auto-generated colors for the given
//! url. The colors are based on the host name
GeminiStyle derive(QUrl const & url) const;
+
+ //! Converts this style into a CSS document for
+ //! non-gemini rendered files.
+ QString toStyleSheet() const;
};
class GeminiDocument :
@@ -60,15 +64,19 @@ public:
QColor background_color;
};
-class GeminiRenderer
+struct GeminiRenderer
{
- GeminiStyle style;
-public:
- GeminiRenderer(GeminiStyle const & style = GeminiStyle{});
-
- std::unique_ptr<GeminiDocument> render(
+ GeminiRenderer() = delete;
+
+ //! Renders the given byte sequence into a GeminiDocument.
+ //! @param input The utf8 encoded input string
+ //! @param root_url The url that is used to resolve relative links
+ //! @param style The style which is used to render the document
+ //! @param outline The extracted outline from the document
+ static std::unique_ptr<GeminiDocument> render(
QByteArray const & input,
QUrl const & root_url,
+ GeminiStyle const & style,
DocumentOutlineModel & outline
);
};
diff --git a/src/kristall.pro b/src/kristall.pro
index ed53750..1cac9ac 100644
--- a/src/kristall.pro
+++ b/src/kristall.pro
@@ -57,4 +57,5 @@ else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
RESOURCES += \
+ ../lib/BreezeStyleSheets/breeze.qrc \
icons.qrc
diff --git a/src/main.cpp b/src/main.cpp
index 5063694..fa22cc1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -6,12 +6,11 @@
int main(int argc, char *argv[])
{
- QApplication a(argc, argv);
- MainWindow w;
+ QApplication app(argc, argv);
- // w.addNewTab(true, QUrl("gemini://gemini.circumlunar.space/"));
+ MainWindow w(&app);
w.addEmptyTab(true, true);
-
w.show();
- return a.exec();
+
+ return app.exec();
}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
index cf12877..e8dbd23 100644
--- a/src/mainwindow.cpp
+++ b/src/mainwindow.cpp
@@ -7,9 +7,12 @@
#include <memory>
#include <QShortcut>
#include <QKeySequence>
+#include <QFile>
+#include <QTextStream>
-MainWindow::MainWindow(QWidget *parent) :
+MainWindow::MainWindow(QApplication * app, QWidget *parent) :
QMainWindow(parent),
+ application(app),
settings("xqTechnologies", "Kristall"),
ui(new Ui::MainWindow),
url_status(new QLabel())
@@ -60,6 +63,8 @@ MainWindow::MainWindow(QWidget *parent) :
}
settings.endGroup();
}
+
+ reloadTheme();
}
MainWindow::~MainWindow()
@@ -206,6 +211,7 @@ void MainWindow::on_actionSettings_triggered()
dialog.setGeminiStyle(this->current_style);
dialog.setStartPage(this->settings.value("start_page").toString());
dialog.setProtocols(this->protocols);
+ dialog.setUiTheme(this->settings.value("theme").toString());
if(dialog.exec() != QDialog::Accepted)
return;
@@ -214,9 +220,13 @@ void MainWindow::on_actionSettings_triggered()
this->settings.setValue("start_page", url.toString());
}
+ this->settings.setValue("theme", dialog.uiTheme());
+
this->protocols = dialog.protocols();
this->current_style = dialog.geminiStyle();
this->saveSettings();
+
+ this->reloadTheme();
}
void MainWindow::on_actionNew_Tab_triggered()
@@ -277,3 +287,25 @@ void MainWindow::on_actionAbout_Qt_triggered()
{
QMessageBox::aboutQt(this, "Kristall");
}
+
+void MainWindow::reloadTheme()
+{
+ QString theme = settings.value("theme").toString();
+ if(theme.isEmpty())
+ theme = "light";
+
+ if(theme == "light")
+ {
+ QFile file(":/light.qss");
+ file.open(QFile::ReadOnly | QFile::Text);
+ QTextStream stream(&file);
+ application->setStyleSheet(stream.readAll());
+ }
+ else if(theme == "dark")
+ {
+ QFile file(":/dark.qss");
+ file.open(QFile::ReadOnly | QFile::Text);
+ QTextStream stream(&file);
+ application->setStyleSheet(stream.readAll());
+ }
+}
diff --git a/src/mainwindow.hpp b/src/mainwindow.hpp
index e207128..7984549 100644
--- a/src/mainwindow.hpp
+++ b/src/mainwindow.hpp
@@ -21,7 +21,7 @@ class MainWindow : public QMainWindow
Q_OBJECT
public:
- MainWindow(QWidget *parent = nullptr);
+ MainWindow(QApplication * app, QWidget *parent = nullptr);
~MainWindow();
BrowserTab * addEmptyTab(bool focus_new, bool load_default);
@@ -67,7 +67,11 @@ private slots:
void on_actionAbout_Qt_triggered();
+private:
+ void reloadTheme();
+
public:
+ QApplication * application;
QSettings settings;
GeminiStyle current_style;
ProtocolSetup protocols;
diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp
index 27a1c12..859f5dd 100644
--- a/src/settingsdialog.cpp
+++ b/src/settingsdialog.cpp
@@ -20,6 +20,10 @@ SettingsDialog::SettingsDialog(QWidget *parent) :
this->ui->auto_theme->addItem("Dark Theme", QVariant::fromValue<int>(GeminiStyle::AutoDarkTheme));
this->ui->auto_theme->addItem("Light Theme", QVariant::fromValue<int>(GeminiStyle::AutoLightTheme));
+ this->ui->ui_theme->clear();
+ this->ui->ui_theme->addItem("Light", QVariant::fromValue<QString>("light"));
+ this->ui->ui_theme->addItem("Dark", QVariant::fromValue<QString>("dark"));
+
setGeminiStyle(GeminiStyle { });
}
@@ -114,7 +118,23 @@ void SettingsDialog::setProtocols(ProtocolSetup const & protocols)
#define M(X) \
this->ui->enable_##X->setChecked(protocols.X);
PROTOCOLS(M)
-#undef M
+ #undef M
+}
+
+QString SettingsDialog::uiTheme() const
+{
+ return this->ui->ui_theme->currentData().toString();
+}
+
+void SettingsDialog::setUiTheme(const QString &theme)
+{
+ if(theme == "light")
+ this->ui->ui_theme->setCurrentIndex(0);
+ else if(theme == "dark")
+ this->ui->ui_theme->setCurrentIndex(1);
+ else
+ this->ui->ui_theme->setCurrentIndex(0);
+
}
void SettingsDialog::reloadStylePreview()
@@ -144,10 +164,13 @@ Plain text document here.
if(host.length() == 0)
host = "preview";
+ QUrl url { QUrl(QString("about://%1/foobar").arg(host)) };
+
DocumentOutlineModel outline;
- auto doc = GeminiRenderer { current_style }.render(
+ auto doc = GeminiRenderer::render(
document,
- QUrl(QString("about://%1/foobar").arg(host)),
+ url,
+ current_style.derive(url),
outline
);
diff --git a/src/settingsdialog.hpp b/src/settingsdialog.hpp
index cc51c96..eb9d68a 100644
--- a/src/settingsdialog.hpp
+++ b/src/settingsdialog.hpp
@@ -30,6 +30,9 @@ public:
ProtocolSetup protocols() const;
void setProtocols(ProtocolSetup const & proto);
+ QString uiTheme() const;
+ void setUiTheme(QString const & theme);
+
private slots:
void on_std_change_font_clicked();
diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui
index a26ae70..58c20e5 100644
--- a/src/settingsdialog.ui
+++ b/src/settingsdialog.ui
@@ -24,28 +24,28 @@
<string>Generic</string>
</attribute>
<layout class="QFormLayout" name="formLayout">
- <item row="0" column="0">
+ <item row="1" column="0">
<widget class="QLabel" name="label_14">
<property name="text">
<string>Start Page:</string>
</property>
</widget>
</item>
- <item row="0" column="1">
+ <item row="1" column="1">
<widget class="QLineEdit" name="start_page">
<property name="placeholderText">
<string>about://blank</string>
</property>
</widget>
</item>
- <item row="1" column="0">
+ <item row="2" column="0">
<widget class="QLabel" name="label_16">
<property name="text">
<string>Enabled Protocols</string>
</property>
</widget>
</item>
- <item row="1" column="1">
+ <item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="enable_gemini">
@@ -103,6 +103,16 @@
</item>
</layout>
</item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_15">
+ <property name="text">
+ <string>UI Theme</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QComboBox" name="ui_theme"/>
+ </item>
</layout>
</widget>
<widget class="QWidget" name="style_tab">