diff --git a/Changelog.txt b/Changelog.txt index c312549..f25625b 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -17,6 +17,12 @@ Kristall Changelog - Added progress display in status bar with loading time and already transferred bytes - Added support for finger:// protocol - Added experimental *highlighting* and _underlining_ for text/gemini +- Desktop file is provided for integrating with XDG +- Fixed bug: Preformatted lines don't break anymore +- text/gemini parsing updated +- Added support for block quotes +- Added help file +- Added shortcut to focus URL bar == 0.1 - Initial release \ No newline at end of file diff --git a/README.md b/README.md index c0aeddc..ab1a1a9 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ A high-quality visual cross-platform gemini browser. ![Site Theme](https://mq32.de/public/7123e22a58969448c27b24df8510f4d56921bf23.png) -## Build Instructions +## Build/Install Instructions ### Requirements @@ -52,7 +52,7 @@ A high-quality visual cross-platform gemini browser. ### Build -## *nix +#### *nix There's a small `Makefile` provided that does all necessary steps and creates a build directory, then copies the build artifact from the build directory. Just do `make` in the root directory, it should work. @@ -69,7 +69,7 @@ make - It seems like Qt wants `libzstd.so.3.1` instead of `libzstd.so.3.2`. Just symlink that file into the build directory - Use `make` and not `gmake` to build the project. -### Notes for Ubuntu 20.04: +##### Notes for Ubuntu 20.04: - Requires packages - `qt5-default` - `qt5-qmake` @@ -77,18 +77,27 @@ make - `make` - `g++` -### Notes for Manjaro/Arch +##### Notes for Manjaro/Arch - Requires packages - `qt5` - `qt5-multimedia` -### Notes on void linux +##### Notes on void linux - set env variable `QT_SELECT=5` -## Windows +#### Windows Just use QtCreator to build `./src/kristall.pro`. Default settings should be fine. +### Manual Installation + +#### Unix / XDG + +The provided desktop file can be installed into the local system +```sh +ln -s Kristall.desktop ~/.local/share/applications/kristall.desktop +``` + ## TODO - [ ] Survive full torture suite - [ ] Correctly parse mime parameters diff --git a/screenshots/linux-i3/video-playback.png b/screenshots/linux-i3/video-playback.png new file mode 100644 index 0000000..e2a0ff5 Binary files /dev/null and b/screenshots/linux-i3/video-playback.png differ diff --git a/src/about/easter-egg.gemini b/src/about/easter-egg.gemini new file mode 100644 index 0000000..c2e9f43 --- /dev/null +++ b/src/about/easter-egg.gemini @@ -0,0 +1,27 @@ +``` + ,`. + ,'` | _.-. + ,` | ,',' / + : | ,',' ; + \ : / / / + \ `.' ( ,' + ,'' _ `. + ,' (o_) `\ + . (,.) _.-- : + -..`/( .-'_..- `| + .-'\,`. `-._ ; + `._ /__ + ,':)-.._ _.(:::`. + |'\ / /`:::| + ,' \ : : : `:| + / : | | | \ + : | | : :..---.: + | | ; ,`._`-.|_ `. + | |' ,'._ `. `. |_\ + | : /`-. `. `. `. : + : \ : __ `. `. `. \ ; + \ \ |. / `. \ \ / + |\ `..: `. __ \ \ / + ' ` .:::::\ `. / \ \,' + .::::::::::-..'_..-' SSt +``` diff --git a/src/about/help.gemini b/src/about/help.gemini new file mode 100644 index 0000000..50b7033 --- /dev/null +++ b/src/about/help.gemini @@ -0,0 +1,44 @@ +# Kristall Help + +This is the manual for the Kristall small-internet browser. It contains explanations on how to use the program, what each setting means and + +## The Mission + +Kristall tries to fill the hole of graphical browsers for alternative internet protocols with a high usability and feature richness. + +## Protocol support + +These protocols are currently supported via their respective URL schemes: +=> https://gemini.circumlunar.space/ Gemini +=> https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol HTTP/HTTPS +=> https://en.wikipedia.org/wiki/Gopher_(protocol) Gopher +=> https://en.wikipedia.org/wiki/Finger_protocol Finger + +### Built-in sites + +There is also the scheme about: which can be used to access internal sites for configuration, usability or help (this is one of them!): +=> about:blank +=> about:favourites +=> about:help + +## Shortcuts + +- Ctrl+T ⇒ New tab +- Ctrl+W ⇒ Close tab +- Ctrl+D ⇒ Quick add/remove from favourites +- Ctrl+L ⇒ Focus URL bar +- Ctrl+S ⇒ Save current file +- Ctrl+H ⇒ Go to home page +- Alt+Left ⇒ Navigate one page back +- Alt+Right ⇒ Navigate one page forward +- F1 ⇒ View this document +- F5 ⇒ Refresh current tab + +## Contact me + +I'm eager to hear from your experience! Did everything work? Is something especially cool or bad? Tell me what you think or what annoys you! + +Please note that everything here is still work-in-progress and may crash! + +Mail: kristall@mq32.de +GitHub: https://github.com/MasterQ32/kristall diff --git a/src/browsertab.cpp b/src/browsertab.cpp index cec5e19..9ecd5aa 100644 --- a/src/browsertab.cpp +++ b/src/browsertab.cpp @@ -166,7 +166,15 @@ void BrowserTab::navigateTo(const QUrl &url, PushToHistory mode) } else { - QMessageBox::warning(this, "Kristall", "Unknown location: " + url.path()); + QFile file(QString(":/about/%1.gemini").arg(url.path())); + if(file.open(QFile::ReadOnly)) + { + this->on_requestComplete(file.readAll(), "text/gemini"); + } + else + { + QMessageBox::warning(this, "Kristall", "Unknown location: " + url.path()); + } } } @@ -233,6 +241,12 @@ void BrowserTab::toggleIsFavourite(bool isFavourite) this->updateUI(); } +void BrowserTab::focusUrlBar() +{ + this->ui->url_bar->setFocus(Qt::ShortcutFocusReason); + this->ui->url_bar->selectAll(); +} + void BrowserTab::on_url_bar_returnPressed() { QUrl url { this->ui->url_bar->text() }; diff --git a/src/browsertab.hpp b/src/browsertab.hpp index 92676d5..68e10fb 100644 --- a/src/browsertab.hpp +++ b/src/browsertab.hpp @@ -54,6 +54,8 @@ public: void toggleIsFavourite(bool isFavourite); + void focusUrlBar(); + signals: void titleChanged(QString const & title); void locationChanged(QUrl const & url); diff --git a/src/documentstyle.cpp b/src/documentstyle.cpp index c7ec17b..369e827 100644 --- a/src/documentstyle.cpp +++ b/src/documentstyle.cpp @@ -125,6 +125,7 @@ DocumentStyle::DocumentStyle() : theme(Fixed), h1_color("#022f90"), h2_color("#022f90"), h3_color("#022f90"), + blockquote_color("#FFFFFF"), internal_link_color("#0e8fff"), external_link_color("#0e8fff"), cross_scheme_link_color("#0960a7"), @@ -164,6 +165,7 @@ bool DocumentStyle::save(QSettings &settings) const settings.setValue("background_color", background_color.name()); settings.setValue("standard_color", standard_color.name()); settings.setValue("preformatted_color", preformatted_color.name()); + settings.setValue("blockquote_color", blockquote_color.name()); settings.setValue("h1_color", h1_color.name()); settings.setValue("h2_color", h2_color.name()); settings.setValue("h3_color", h3_color.name()); @@ -196,6 +198,7 @@ bool DocumentStyle::load(QSettings &settings) background_color = QColor(settings.value("background_color").toString()); standard_color = QColor(settings.value("standard_color").toString()); preformatted_color = QColor(settings.value("preformatted_color").toString()); + blockquote_color = QColor(settings.value("blockquote_color").toString()); h1_color = QColor(settings.value("h1_color").toString()); h2_color = QColor(settings.value("h2_color").toString()); h3_color = QColor(settings.value("h3_color").toString()); @@ -245,6 +248,8 @@ DocumentStyle DocumentStyle::derive(const QUrl &url) const themed.internal_link_color = themed.external_link_color.lighter(110); themed.cross_scheme_link_color = themed.external_link_color.darker(110); + themed.blockquote_color = themed.background_color.lighter(130); + break; } @@ -261,6 +266,8 @@ DocumentStyle DocumentStyle::derive(const QUrl &url) const themed.internal_link_color = themed.external_link_color.darker(110); themed.cross_scheme_link_color = themed.external_link_color.lighter(110); + themed.blockquote_color = themed.background_color.darker(120); + break; } diff --git a/src/documentstyle.hpp b/src/documentstyle.hpp index db02a36..6692cfd 100644 --- a/src/documentstyle.hpp +++ b/src/documentstyle.hpp @@ -30,6 +30,7 @@ struct DocumentStyle QColor h1_color; QColor h2_color; QColor h3_color; + QColor blockquote_color; QColor internal_link_color; QColor external_link_color; diff --git a/src/geminirenderer.cpp b/src/geminirenderer.cpp index fd475ce..d497a6c 100644 --- a/src/geminirenderer.cpp +++ b/src/geminirenderer.cpp @@ -29,9 +29,6 @@ std::unique_ptr GeminiRenderer::render( DocumentStyle const & themed_style, DocumentOutlineModel &outline) { - QTextOption no_wrap; - no_wrap.setWrapMode(QTextOption::NoWrap); - QTextCharFormat preformatted; preformatted.setFont(themed_style.preformatted_font); preformatted.setForeground(themed_style.preformatted_color); @@ -67,17 +64,25 @@ std::unique_ptr GeminiRenderer::render( std::unique_ptr result = std::make_unique(); result->setDocumentMargin(themed_style.margin); result->background_color = themed_style.background_color; - result->setDefaultTextOption(no_wrap); - + result->setIndentWidth(20); bool emit_fancy_text = global_settings.value("text_decoration").toBool(); QTextCursor cursor{result.get()}; - QTextBlockFormat non_list_format = cursor.blockFormat(); + QTextBlockFormat standard_format = cursor.blockFormat(); + + QTextBlockFormat preformatted_format = standard_format; + preformatted_format.setNonBreakableLines(true); + + QTextBlockFormat block_quote_format = standard_format; + block_quote_format.setIndent(1); + block_quote_format.setBackground(themed_style.blockquote_color); + bool verbatim = false; QTextList *current_list = nullptr; + bool blockquote = false; outline.beginBuild(); @@ -94,18 +99,19 @@ std::unique_ptr GeminiRenderer::render( { if (line.startsWith("```")) { + cursor.setBlockFormat(standard_format); verbatim = false; } else { - cursor.block().layout()->setTextOption(no_wrap); + cursor.setBlockFormat(preformatted_format); cursor.setCharFormat(preformatted); cursor.insertText(line + "\n"); } } else { - if (line.startsWith("*")) + if (line.startsWith("* ")) { if (current_list == nullptr) { @@ -127,11 +133,31 @@ std::unique_ptr GeminiRenderer::render( if (current_list != nullptr) { cursor.insertBlock(); - cursor.setBlockFormat(non_list_format); + cursor.setBlockFormat(standard_format); } current_list = nullptr; } + if(line.startsWith(">")) + { + if(not blockquote ) { + // cursor.insertBlock(); + } + blockquote = true; + + cursor.setBlockFormat(block_quote_format); + cursor.insertText(trim_whitespace(line.mid(1)) + "\n", standard); + + continue; + } + else + { + if(blockquote) { + cursor.setBlockFormat(standard_format); + } + blockquote = false; + } + if (line.startsWith("###")) { auto heading = trim_whitespace(line.mid(3)); @@ -234,6 +260,8 @@ std::unique_ptr GeminiRenderer::render( { if(emit_fancy_text) { + // TODO: Fix UTF-8 encoding here… Don't emit single characters but always spans! + bool rendering_bold = false; bool rendering_underlined = false; diff --git a/src/icons.qrc b/src/icons.qrc index a96f1f8..22c2d11 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -34,5 +34,8 @@ icons/gopher/telnet.svg icons/gopher/text.svg icons/info.svg + about/help.gemini + about/easter-egg.gemini + icons/help-box.svg diff --git a/src/icons/help-box.svg b/src/icons/help-box.svg new file mode 100644 index 0000000..7a08fc4 --- /dev/null +++ b/src/icons/help-box.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 5bf6211..c1361dd 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -65,6 +65,11 @@ MainWindow::MainWindow(QApplication * app, QWidget *parent) : } }); + { + QShortcut * sc = new QShortcut(QKeySequence("Ctrl+L"), this); + connect(sc, &QShortcut::activated, this, &MainWindow::on_focus_inputbar); + } + { global_settings.beginGroup("Window State"); if(global_settings.contains("geometry")) { @@ -378,3 +383,16 @@ void MainWindow::on_tab_fileLoaded(qint64 fileSize, const QString &mime, int mse } } } + +void MainWindow::on_focus_inputbar() +{ + BrowserTab * tab = qobject_cast(this->ui->browser_tabs->currentWidget()); + if(tab != nullptr) { + tab->focusUrlBar(); + } +} + +void MainWindow::on_actionHelp_triggered() +{ + this->addNewTab(true, QUrl("about:help")); +} diff --git a/src/mainwindow.hpp b/src/mainwindow.hpp index f619056..2b5e2ef 100644 --- a/src/mainwindow.hpp +++ b/src/mainwindow.hpp @@ -75,6 +75,10 @@ private slots: void on_tab_fileLoaded(qint64 fileSize, QString const & mime, int msec); + void on_focus_inputbar(); + + void on_actionHelp_triggered(); + private: void reloadTheme(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui index 62c05e5..199547c 100644 --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -205,6 +205,8 @@ Help + + @@ -349,6 +351,21 @@ Go to home + + Ctrl+H + + + + + + :/icons/help-box.svg:/icons/help-box.svg + + + Help + + + F1 + diff --git a/src/settingsdialog.cpp b/src/settingsdialog.cpp index d6b84cf..6512db6 100644 --- a/src/settingsdialog.cpp +++ b/src/settingsdialog.cpp @@ -108,6 +108,10 @@ void SettingsDialog::setGeminiStyle(DocumentStyle const &style) .arg(this->current_style.background_color.name()) .arg("#FF00FF")); + ui->quote_preview->setStyleSheet(COLOR_STYLE + .arg(this->current_style.blockquote_color.name()) + .arg("#FF00FF")); + ui->link_local_preview->setStyleSheet(COLOR_STYLE .arg(this->current_style.background_color.name()) .arg(this->current_style.internal_link_color.name())); @@ -184,6 +188,8 @@ Plain text document here. => rela-link Same-Site Link => //foreign.host/ Foreign Site Link => https://foreign.host/ Cross-Protocol Link +> Multi-lined +> block quotes ``` ▄▄▄ ██▀███ ▄▄▄█████▓ ▒████▄ ▓██ ▒ ██▒▓ ██▒ ▓▒ @@ -309,6 +315,10 @@ void SettingsDialog::on_link_cross_change_color_clicked() { updateColor(current_style.cross_scheme_link_color); } +void SettingsDialog::on_quote_change_color_clicked() +{ + updateColor(current_style.blockquote_color); +} void SettingsDialog::on_link_local_prefix_textChanged(const QString &text) { diff --git a/src/settingsdialog.hpp b/src/settingsdialog.hpp index 5aa2c77..640cea9 100644 --- a/src/settingsdialog.hpp +++ b/src/settingsdialog.hpp @@ -79,6 +79,8 @@ private slots: void on_SettingsDialog_accepted(); + void on_quote_change_color_clicked(); + private: void reloadStylePreview(); diff --git a/src/settingsdialog.ui b/src/settingsdialog.ui index 57c5674..1089f05 100644 --- a/src/settingsdialog.ui +++ b/src/settingsdialog.ui @@ -7,7 +7,7 @@ 0 0 850 - 540 + 650 @@ -17,7 +17,7 @@ - 0 + 1 @@ -591,24 +591,24 @@ - + Auto-Theme Generation - + - + Page Margin - + px @@ -621,7 +621,7 @@ - + @@ -701,13 +701,42 @@ - + Presets + + + + Block Quote Background + + + + + + + + + + + + + + + + ... + + + + :/icons/palette.svg:/icons/palette.svg + + + + +