Adds help document, adds block quote support, updates gemini parser to newest spec, adds support for arbitrary gemini files in about: space, adds url bar shortcut, fixes bug with line breaks in preformatted text
This commit is contained in:
parent
573e73eb1b
commit
df4fbcb4cf
|
@ -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
|
21
README.md
21
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
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 179 KiB |
|
@ -0,0 +1,27 @@
|
|||
```
|
||||
,`.
|
||||
,'` | _.-.
|
||||
,` | ,',' /
|
||||
: | ,',' ;
|
||||
\ : / / /
|
||||
\ `.' ( ,'
|
||||
,'' _ `.
|
||||
,' (o_) `\
|
||||
. (,.) _.-- :
|
||||
-..`/( .-'_..- `|
|
||||
.-'\,`. `-._ ;
|
||||
`._ /__
|
||||
,':)-.._ _.(:::`.
|
||||
|'\ / /`:::|
|
||||
,' \ : : : `:|
|
||||
/ : | | | \
|
||||
: | | : :..---.:
|
||||
| | ; ,`._`-.|_ `.
|
||||
| |' ,'._ `. `. |_\
|
||||
| : /`-. `. `. `. :
|
||||
: \ : __ `. `. `. \ ;
|
||||
\ \ |. / `. \ \ /
|
||||
|\ `..: `. __ \ \ /
|
||||
' ` .:::::\ `. / \ \,'
|
||||
.::::::::::-..'_..-' SSt
|
||||
```
|
|
@ -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
|
|
@ -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() };
|
||||
|
|
|
@ -54,6 +54,8 @@ public:
|
|||
|
||||
void toggleIsFavourite(bool isFavourite);
|
||||
|
||||
void focusUrlBar();
|
||||
|
||||
signals:
|
||||
void titleChanged(QString const & title);
|
||||
void locationChanged(QUrl const & url);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -29,9 +29,6 @@ std::unique_ptr<GeminiDocument> 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<GeminiDocument> GeminiRenderer::render(
|
|||
std::unique_ptr<GeminiDocument> result = std::make_unique<GeminiDocument>();
|
||||
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<GeminiDocument> 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<GeminiDocument> 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<GeminiDocument> 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;
|
||||
|
||||
|
|
|
@ -34,5 +34,8 @@
|
|||
<file>icons/gopher/telnet.svg</file>
|
||||
<file>icons/gopher/text.svg</file>
|
||||
<file>icons/info.svg</file>
|
||||
<file>about/help.gemini</file>
|
||||
<file>about/easter-egg.gemini</file>
|
||||
<file>icons/help-box.svg</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M11,18H13V16H11V18M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6M5,3H19A2,2 0 0,1 21,5V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V5A2,2 0 0,1 5,3Z" /></svg>
|
After Width: | Height: | Size: 504 B |
|
@ -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<BrowserTab*>(this->ui->browser_tabs->currentWidget());
|
||||
if(tab != nullptr) {
|
||||
tab->focusUrlBar();
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::on_actionHelp_triggered()
|
||||
{
|
||||
this->addNewTab(true, QUrl("about:help"));
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -205,6 +205,8 @@
|
|||
<property name="title">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<addaction name="actionHelp"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionAbout"/>
|
||||
<addaction name="actionAbout_Qt"/>
|
||||
</widget>
|
||||
|
@ -349,6 +351,21 @@
|
|||
<property name="text">
|
||||
<string>Go to home</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+H</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionHelp">
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/help-box.svg</normaloff>:/icons/help-box.svg</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Help</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>F1</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<resources>
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -79,6 +79,8 @@ private slots:
|
|||
|
||||
void on_SettingsDialog_accepted();
|
||||
|
||||
void on_quote_change_color_clicked();
|
||||
|
||||
private:
|
||||
void reloadStylePreview();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>850</width>
|
||||
<height>540</height>
|
||||
<height>650</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
|
@ -17,7 +17,7 @@
|
|||
<item>
|
||||
<widget class="QTabWidget" name="tabWidget">
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
<number>1</number>
|
||||
</property>
|
||||
<widget class="QWidget" name="generic">
|
||||
<attribute name="title">
|
||||
|
@ -591,24 +591,24 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Auto-Theme Generation</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<item row="12" column="1">
|
||||
<widget class="QComboBox" name="auto_theme"/>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<item row="13" column="0">
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Page Margin</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<item row="13" column="1">
|
||||
<widget class="QDoubleSpinBox" name="page_margin">
|
||||
<property name="suffix">
|
||||
<string> px</string>
|
||||
|
@ -621,7 +621,7 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<item row="14" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="presets"/>
|
||||
|
@ -701,13 +701,42 @@
|
|||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="13" column="0">
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Presets</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_21">
|
||||
<property name="text">
|
||||
<string>Block Quote Background</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QLabel" name="quote_preview">
|
||||
<property name="text">
|
||||
<string> </string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="quote_change_color">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="icons.qrc">
|
||||
<normaloff>:/icons/palette.svg</normaloff>:/icons/palette.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
|
|
Loading…
Reference in New Issue