aboutsummaryrefslogtreecommitdiff
path: root/src/mainwindow
diff options
context:
space:
mode:
Diffstat (limited to 'src/mainwindow')
-rw-r--r--src/mainwindow/mainwindow.cpp316
-rw-r--r--src/mainwindow/mainwindow.h98
-rw-r--r--src/mainwindow/mainwindow.ui63
-rw-r--r--src/mainwindow/widgets/loadingbar.cpp50
-rw-r--r--src/mainwindow/widgets/loadingbar.h30
-rw-r--r--src/mainwindow/widgets/navigationbar.cpp115
-rw-r--r--src/mainwindow/widgets/navigationbar.h55
7 files changed, 727 insertions, 0 deletions
diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp
new file mode 100644
index 0000000..018ff64
--- /dev/null
+++ b/src/mainwindow/mainwindow.cpp
@@ -0,0 +1,316 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "mainwindow.h"
+#include "forms/aboutdialog.h"
+#include "forms/searchform.h"
+#include "ui_mainwindow.h"
+#include "widgets/mainwindowmenubar.h"
+#include <QDockWidget>
+#include <QMessageBox>
+#include <bookmarks/bookmarkswidget.h>
+#include <downloads/downloadswidget.h>
+#include <navigation/urllineedit.h>
+#include <settings/settingsdialog.h>
+
+MainWindow::MainWindow(std::shared_ptr<Configuration> config, QWidget *parent)
+ : QMainWindow(parent)
+ , ui(new Ui::MainWindow)
+ , tabBar(new MainWindowTabBar(config, this))
+ , menuBar(new MainWindowMenuBar(config, this))
+ , m_addressBar(new UrlLineEdit(this))
+ , m_progressBar(new LoadingBar(this))
+{
+ Q_ASSERT(config);
+ m_config = config;
+
+ // delete this window when it closes
+ setAttribute(Qt::WA_DeleteOnClose, true);
+
+ // set up UI
+ ui->setupUi(this);
+
+ QAction *fullscreenAction = new QAction(this);
+ fullscreenAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("browser.shortcuts.fullscreen").value())));
+ connect(fullscreenAction, &QAction::triggered, this, &MainWindow::toggleFullscreen);
+ addAction(fullscreenAction);
+
+ // Dockable widget styling
+ setDockOptions(dockOptions() | AllowTabbedDocks | ForceTabbedDocks);
+ setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North);
+ setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North);
+
+ // Add the toolbars
+ // tabToolBar: main menu and tab list
+ ui->mainToolBar->setMovable(m_config->value<bool>("browser.ui.tabtoolbarMovable").value());
+ ui->mainToolBar->addWidget(menuBar);
+ //tabToolBar->addWidget(tabBar);
+
+ // navigationToolBar: address bar
+ ui->navigationToolBar->setMovable(m_config->value<bool>("browser.ui.navtoolbarMovable").value());
+ insertToolBarBreak(ui->navigationToolBar);
+
+ // page actions
+ m_navigationBar = new NavigationBar(this);
+ m_navigationBar->addWidgetsTo(ui->navigationToolBar);
+
+ ui->navigationToolBar->addWidget(m_addressBar);
+
+ // connect signals
+ connect(m_addressBar, &UrlLineEdit::addressEntered, this, [&](const QUrl &url) {
+ tabBar->currentView()->load(url);
+ });
+ connect(m_addressBar, &UrlLineEdit::searchTermEntered, this, [&](const QString &term) {
+ QString t = term;
+ t.replace(' ', '+');
+ QString url = QString::fromStdString(m_config->value<std::string>("profile.search").value());
+ url.replace("$term", t);
+ tabBar->currentView()->load(QUrl::fromUserInput(url));
+ });
+ connect(tabBar, &MainWindowTabBar::currentTabChanged, this, &MainWindow::handleTabChanged);
+ //connect(tabBar, SIGNAL(currentTabChanged(WebView *)), this, SLOT(handleTabChanged(WebView *)));
+
+ // loading bar
+ ui->statusBar->addPermanentWidget(m_progressBar);
+
+ // search box
+ m_searchBox = new SearchForm(this);
+ ui->statusBar->addWidget(m_searchBox);
+ m_searchBox->setVisible(false);
+
+ // shortcuts
+ QAction *focusAddressAction = new QAction(this);
+ focusAddressAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("browser.shortcuts.focusAddress").value())));
+ //focusAddressAction->setShortcut(QKeySequence::fromString(browser->settings()->value("window.shortcuts.focusAddress").toString()));
+ //connect(focusAddressAction, SIGNAL(triggered(bool)), this, SLOT(focusAddress()));
+ connect(focusAddressAction, &QAction::triggered, this, [this]() {
+ m_addressBar->setFocus();
+ m_addressBar->selectAll();
+ });
+ addAction(focusAddressAction);
+
+ resize(m_config->value<int>("browser.window.width").value(), m_config->value<int>("browser.window.height").value());
+ if(m_config->value<bool>("browser.window.maximized").value()) {
+ showMaximized();
+ }
+}
+
+MainWindow::~MainWindow()
+{
+ // Release all dock widgets before deleting so we don't accidentally delete them
+ // Also fixes that annoying crash when closing
+ QList<QDockWidget *> allDockWidgets = findChildren<QDockWidget *>();
+ for(QDockWidget *w : allDockWidgets) {
+ if(w->widget()) {
+ w->widget()->setParent(nullptr);
+ }
+ }
+
+ delete ui;
+}
+
+void MainWindow::addTabbedDock(Qt::DockWidgetArea area, QWidget *widget)
+{
+ // make a list of widgets in the area we want
+ // this way we can append the new dock widget to the last one
+ QVector<QDockWidget *> areaDockWidgets;
+ for(QDockWidget *w : findChildren<QDockWidget *>()) {
+ // check if widget is already shown
+ if(w->widget() == widget) {
+ // in this case, close the dock and return
+ w->close();
+ return;
+ }
+
+ if(dockWidgetArea(w) == area) {
+ areaDockWidgets.append(w);
+ }
+ }
+
+ // create a dock widget
+ QDockWidget *dock = new QDockWidget(widget->windowTitle(), this);
+ dock->setAttribute(Qt::WA_DeleteOnClose, true);
+
+ // when the dock widget becomes invisble, release the docked widget
+ // setting the widget makes the dock its parent; setting parent back to nullptr
+ // makes the dock not show the widget any more
+ connect(dock, &QDockWidget::visibilityChanged, [dock](bool visible) {
+ if(!visible && dock->widget()) {
+ dock->widget()->setParent(nullptr);
+ }
+ });
+ dock->setWidget(widget);
+
+ // the same widget may be shown by docks in other windows
+ // in that case, they grab ownership and the current dock won't be showing anything
+ // so the current widget needs to be closed
+ auto *w = dynamic_cast<BookmarksWidget *>(widget);
+ w->closeOthers();
+ if(w) {
+ connect(w, &BookmarksWidget::closeOthersSignal, dock, [dock]() {
+ dock->close();
+ });
+ }
+
+ if(areaDockWidgets.empty()) {
+ // no other widgets
+ addDockWidget(area, dock);
+ } else {
+ // there are other widgets, so put it after the last one
+ tabifyDockWidget(areaDockWidgets.last(), dock);
+ }
+}
+
+void MainWindow::newTab(const QUrl &url)
+{
+ if(!m_tabBarAdded) {
+ m_tabBarAdded = true;
+ ui->mainToolBar->addWidget(tabBar);
+ }
+ tabBar->addTab(createWebView(url, m_profile.get(), this));
+}
+
+MainWindow *MainWindow::newWindow(const QUrl &url)
+{
+ Browser *instance = static_cast<Browser *>(qApp->instance());
+ return instance->createSession(m_profile->storageName(), true, QStringList(url.toString()));
+}
+
+void MainWindow::closeEvent(QCloseEvent *event)
+{
+ if(tabBar->count() > 1) {
+ int ret = QMessageBox::warning(this, tr("Close window?"), tr("Close multiple tabs?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ if(ret == QMessageBox::No) {
+ event->ignore();
+ return;
+ }
+ }
+ QMainWindow::closeEvent(event);
+}
+
+void MainWindow::about()
+{
+ AboutDialog *dlg = new AboutDialog(this);
+ dlg->exec();
+}
+
+void MainWindow::showSettingsDialog()
+{
+ SettingsDialog *dlg = new SettingsDialog(m_config, this);
+ dlg->exec();
+}
+
+void MainWindow::setProfile(std::shared_ptr<WebEngineProfile> profile)
+{
+ Q_ASSERT(profile);
+ m_profile = profile;
+ tabBar->setProfile(profile.get());
+ menuBar->setProfileName(profile->name());
+}
+
+WebEngineProfile *MainWindow::profile()
+{
+ Q_ASSERT(m_profile);
+ return m_profile.get();
+}
+
+void MainWindow::setBookmarksWidget(std::shared_ptr<BookmarksWidget> &widget)
+{
+ Q_ASSERT(widget);
+ m_bookmarksWidget = widget;
+ m_addressBar->setCompleterModel(m_bookmarksWidget->model());
+ connect(menuBar->bookmarksAction(), &QAction::triggered, this, [this]() {
+ addTabbedDock(Qt::RightDockWidgetArea, m_bookmarksWidget.get());
+ });
+ connect(m_bookmarksWidget.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) {
+ if(isActiveWindow()) {
+ newTab(url);
+ }
+ });
+}
+
+void MainWindow::setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget)
+{
+ Q_ASSERT(widget);
+ m_downloadsWidget = widget;
+}
+
+void MainWindow::toggleFullscreen()
+{
+ if(isFullScreen()) {
+ setWindowState(Qt::WindowMaximized | Qt::WindowActive);
+ } else {
+ setWindowState(Qt::WindowFullScreen | Qt::WindowActive);
+ }
+}
+
+void MainWindow::handleTabChanged(WebView *view)
+{
+ Q_CHECK_PTR(view);
+
+ m_currentView = view;
+
+ // centralWidget can be a nullptr
+ if(centralWidget()) {
+ // clear the parent of the central widget so it doesn't get deleted
+ centralWidget()->setParent(nullptr);
+
+ // disconnect signals
+ disconnect(centralWidget());
+ }
+
+ // set new central widget
+ setCentralWidget(view);
+
+ // connect signals
+ m_navigationBar->connectWebView(view);
+
+ connect(view, &WebView::urlChanged, m_addressBar, &UrlLineEdit::setUrl);
+ m_addressBar->setUrl(view->url());
+ m_addressBar->pageAction()->setMenu(view->pageMenu());
+
+ connect(view, &WebView::titleChanged, this, &MainWindow::handleTitleUpdated);
+
+ m_progressBar->connectWebView(view);
+
+ // update UI
+ this->handleTitleUpdated(view->title());
+ centralWidget()->setFocus();
+}
+
+void MainWindow::handleTitleUpdated(const QString &title)
+{
+ QString t = QString::fromStdString(m_config->value<std::string>("browser.window.title").value());
+ t.replace("title", title);
+ t.replace("profile", m_profile->name());
+ setWindowTitle(t);
+ //setWindowTitle(browser->settings()->value("window.title").toString().replace("title", title).replace("profile", tabBar->profile()->name()));
+}
+
+void MainWindow::addPlugins(const QVector<Browser::Plugin> &plugins)
+{
+ for(const Browser::Plugin &plugin : plugins) {
+ ProfileInterface *iProfilePlugin = qobject_cast<ProfileInterface *>(plugin.pointer);
+ if(iProfilePlugin) {
+ QWidget *w = iProfilePlugin->createWidget(m_profile.get(), this);
+
+ auto *profileAction = new QAction(tr("Profile Action"), this);
+ ui->navigationToolBar->addAction(profileAction);
+ connect(profileAction, &QAction::triggered, this, [this, w]() {
+ w->setVisible(!w->isVisible());
+ if(w->isVisible()) {
+ QPoint pos = ui->navigationToolBar->pos();
+ pos.setX(pos.x() + ui->navigationToolBar->width() - w->width());
+ pos.setY(pos.y() + ui->navigationToolBar->height());
+ w->move(mapToGlobal(pos));
+ w->show();
+ }
+ });
+ }
+ }
+}
diff --git a/src/mainwindow/mainwindow.h b/src/mainwindow/mainwindow.h
new file mode 100644
index 0000000..3f2c3a2
--- /dev/null
+++ b/src/mainwindow/mainwindow.h
@@ -0,0 +1,98 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef MAINWINDOW_H
+#define MAINWINDOW_H
+
+#include "browser.h"
+#include "webengine/webengineprofile.h"
+#include "widgets/loadingbar.h"
+#include "widgets/navigationbar.h"
+#include "widgets/mainwindowtabbar.h"
+#include <QMainWindow>
+#include <QUrl>
+#include <interfaces.h>
+#include <memory>
+
+namespace Ui
+{
+class MainWindow;
+}
+
+class SearchForm;
+
+class Configuration;
+class BookmarksWidget;
+class DownloadsWidget;
+
+class MainWindowMenuBar;
+class UrlLineEdit;
+
+class MainWindow : public QMainWindow
+{
+ Q_OBJECT
+
+ friend class WebView;
+ friend class SearchForm;
+
+ friend class MainWindowMenuBar;
+
+ friend class NavigationBar;
+
+public:
+ explicit MainWindow(std::shared_ptr<Configuration> config, QWidget *parent = nullptr);
+ Q_DISABLE_COPY(MainWindow)
+ ~MainWindow() override;
+
+ void addTabbedDock(Qt::DockWidgetArea area, QWidget *widget);
+
+public slots:
+ void about();
+ void showSettingsDialog();
+
+ void newTab(const QUrl &url = QUrl(""));
+ MainWindow *newWindow(const QUrl &url = QUrl(""));
+
+ void setProfile(std::shared_ptr<WebEngineProfile> profile);
+ WebEngineProfile *profile();
+
+ void setBookmarksWidget(std::shared_ptr<BookmarksWidget> &widget);
+ void setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget);
+ void addPlugins(const QVector<Browser::Plugin> &plugins);
+
+ void toggleFullscreen();
+
+protected:
+ void closeEvent(QCloseEvent *event) override;
+
+private slots:
+ void handleTabChanged(WebView *view);
+ void handleTitleUpdated(const QString &title);
+
+private:
+ Ui::MainWindow *ui;
+ SearchForm *m_searchBox;
+
+ MainWindowTabBar *tabBar;
+ WebView *m_currentView;
+
+ MainWindowMenuBar *menuBar;
+
+ // navigation
+ NavigationBar *m_navigationBar;
+ UrlLineEdit *m_addressBar;
+ LoadingBar *m_progressBar;
+
+ bool m_tabBarAdded = false;
+ std::shared_ptr<WebEngineProfile> m_profile;
+ std::shared_ptr<Configuration> m_config;
+ std::shared_ptr<BookmarksWidget> m_bookmarksWidget;
+ std::shared_ptr<DownloadsWidget> m_downloadsWidget;
+};
+
+#endif // MAINWINDOW_H
diff --git a/src/mainwindow/mainwindow.ui b/src/mainwindow/mainwindow.ui
new file mode 100644
index 0000000..12a3a6c
--- /dev/null
+++ b/src/mainwindow/mainwindow.ui
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>MainWindow</string>
+ </property>
+ <widget class="QWidget" name="centralWidget"/>
+ <widget class="QStatusBar" name="statusBar">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>24</height>
+ </size>
+ </property>
+ </widget>
+ <widget class="QToolBar" name="mainToolBar">
+ <property name="windowTitle">
+ <string>Main</string>
+ </property>
+ <property name="allowedAreas">
+ <set>Qt::TopToolBarArea</set>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ <widget class="QToolBar" name="navigationToolBar">
+ <property name="windowTitle">
+ <string>Navigation</string>
+ </property>
+ <property name="allowedAreas">
+ <set>Qt::TopToolBarArea</set>
+ </property>
+ <attribute name="toolBarArea">
+ <enum>TopToolBarArea</enum>
+ </attribute>
+ <attribute name="toolBarBreak">
+ <bool>false</bool>
+ </attribute>
+ </widget>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/mainwindow/widgets/loadingbar.cpp b/src/mainwindow/widgets/loadingbar.cpp
new file mode 100644
index 0000000..99f44d2
--- /dev/null
+++ b/src/mainwindow/widgets/loadingbar.cpp
@@ -0,0 +1,50 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "loadingbar.h"
+#include <QTimer>
+#include "webengine/webview.h"
+
+LoadingBar::LoadingBar(QWidget *parent)
+ : QProgressBar(parent)
+{
+ setMaximum(100);
+}
+
+void LoadingBar::connectWebView(WebView *view)
+{
+ Q_CHECK_PTR(view);
+
+ disconnect(loadStartedConnection);
+ disconnect(loadProgressConnection);
+ disconnect(loadFinishedConnection);
+
+ if(view->isLoaded()) {
+ this->hide();
+ } else {
+ loadStarted();
+ setValue(view->loadProgress());
+ }
+
+ loadStartedConnection = connect(view, &QWebEngineView::loadStarted, this, &LoadingBar::loadStarted);
+ loadProgressConnection = connect(view, &QWebEngineView::loadProgress, this, &QProgressBar::setValue);
+ loadFinishedConnection = connect(view, &QWebEngineView::loadFinished, this, &LoadingBar::loadFinished);
+}
+
+void LoadingBar::loadStarted()
+{
+ resetFormat();
+ show();
+ setValue(0);
+}
+
+void LoadingBar::loadFinished(bool ok)
+{
+ setFormat(QString("%p% %1").arg(ok ? tr("Finished") : tr("Failed")));
+ QTimer::singleShot(2000, this, SLOT(hide()));
+}
diff --git a/src/mainwindow/widgets/loadingbar.h b/src/mainwindow/widgets/loadingbar.h
new file mode 100644
index 0000000..8ae314e
--- /dev/null
+++ b/src/mainwindow/widgets/loadingbar.h
@@ -0,0 +1,30 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef LOADINGBAR_H
+#define LOADINGBAR_H
+
+#include <QProgressBar>
+
+class WebView;
+class LoadingBar : public QProgressBar
+{
+ Q_OBJECT
+public:
+ explicit LoadingBar(QWidget *parent = nullptr);
+ void connectWebView(WebView *view);
+
+private slots:
+ void loadStarted();
+ void loadFinished(bool ok);
+
+private:
+ QMetaObject::Connection loadStartedConnection, loadProgressConnection, loadFinishedConnection;
+};
+
+#endif // LOADINGBAR_H
diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp
new file mode 100644
index 0000000..648bb23
--- /dev/null
+++ b/src/mainwindow/widgets/navigationbar.cpp
@@ -0,0 +1,115 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "navigationbar.h"
+#include "mainwindow/mainwindow.h"
+#include "webengine/webview.h"
+#include <QHBoxLayout>
+#include <QStyle>
+#include <QToolBar>
+#include <QToolButton>
+#include <QWebEngineHistory>
+
+NavigationBar::NavigationBar(MainWindow *parent)
+ : QObject(parent)
+{
+ qStyle = parent->style();
+
+ backButton = new QToolButton(parent);
+ backButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowBack));
+ connect(backButton, &QToolButton::clicked, this, [this]() {
+ m_view->history()->back();
+ });
+
+ auto *backMenu = new QMenu(backButton);
+ backButton->setMenu(backMenu);
+ connect(backMenu, &QMenu::aboutToShow, this, [this, backMenu]() {
+ backMenu->clear();
+ const QList<QWebEngineHistoryItem> items = m_view->history()->backItems(10);
+ for(const QWebEngineHistoryItem &i : items) {
+ QAction *a = backMenu->addAction(i.title());
+ connect(a, &QAction::triggered, this, [i, this]() {
+ m_view->history()->goToItem(i);
+ });
+ }
+ });
+
+ forwardButton = new QToolButton(parent);
+ forwardButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowForward));
+ connect(forwardButton, &QToolButton::clicked, this, [this]() {
+ m_view->history()->forward();
+ });
+
+ auto *forwardMenu = new QMenu(forwardButton);
+ forwardButton->setMenu(forwardMenu);
+ connect(forwardMenu, &QMenu::aboutToShow, this, [this, forwardMenu]() {
+ forwardMenu->clear();
+ const QList<QWebEngineHistoryItem> items = m_view->history()->forwardItems(10);
+ for(const QWebEngineHistoryItem &i : items) {
+ QAction *a = forwardMenu->addAction(i.title());
+ connect(a, &QAction::triggered, this, [i, this]() {
+ m_view->history()->goToItem(i);
+ });
+ }
+ });
+
+ stopReloadButton = new QToolButton(parent);
+ stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload));
+ connect(stopReloadButton, &QToolButton::clicked, this, [this]() {
+ if(m_view->isLoaded())
+ m_view->reload();
+ else
+ m_view->stop();
+ });
+
+ homeButton = new QToolButton(parent);
+ homeButton->setIcon(qStyle->standardIcon(QStyle::SP_DirHomeIcon));
+ connect(homeButton, &QToolButton::clicked, this, [this, parent]() {
+ m_view->load(parent->m_profile->homepage());
+ });
+}
+
+void NavigationBar::addWidgetsTo(QToolBar *toolBar)
+{
+ toolBar->addWidget(backButton);
+ toolBar->addWidget(forwardButton);
+ toolBar->addWidget(stopReloadButton);
+ toolBar->addWidget(homeButton);
+}
+
+void NavigationBar::connectWebView(WebView *view)
+{
+ Q_CHECK_PTR(view);
+ m_view = view;
+
+ disconnect(loadStartedConnection);
+ disconnect(loadFinishedConnection);
+
+ if(view->isLoaded()) {
+ update_loadFinished();
+ } else {
+ update_loadStarted();
+ }
+
+ loadStartedConnection = connect(view, &QWebEngineView::loadStarted, this, &NavigationBar::update_loadStarted);
+ loadFinishedConnection = connect(view, &QWebEngineView::loadFinished, this, &NavigationBar::update_loadFinished);
+}
+
+void NavigationBar::update_loadStarted()
+{
+ backButton->setEnabled(m_view->history()->canGoForward());
+ forwardButton->setEnabled(m_view->history()->canGoForward());
+ stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserStop));
+}
+
+void NavigationBar::update_loadFinished()
+{
+ backButton->setEnabled(m_view->history()->canGoBack());
+ forwardButton->setEnabled(m_view->history()->canGoForward());
+ stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload));
+}
diff --git a/src/mainwindow/widgets/navigationbar.h b/src/mainwindow/widgets/navigationbar.h
new file mode 100644
index 0000000..15a9c7b
--- /dev/null
+++ b/src/mainwindow/widgets/navigationbar.h
@@ -0,0 +1,55 @@
+/*
+ * This file is part of smolbote. It's copyrighted by the contributors recorded
+ * in the version control history of the file, available from its original
+ * location: git://neueland.iserlohn-fortress.net/smolbote.git
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+/*
+ * Why is this class a QObject and not a QWidget, and why don't we add the
+ * NavigationBar itself to the toolbar?
+ * That was the original idea: make a NavBar class, friend it from MainWindow
+ * to gain access to the config, but there's a couple of issues:
+ * - adding QToolButtons to a widget that's inside a toolbar makes the buttons
+ * look absolutely hideous: they're smaller and have a border for some reason
+ * - if you stylesheet the border away (which is a pain), they're still not
+ * the same size
+ * And so we ended up having this class only exist to contain all of the logic
+ * for the nav buttons (which cuts down the code in MainWindow).
+ */
+
+#ifndef NAVIGATIONBAR_H
+#define NAVIGATIONBAR_H
+
+#include <QObject>
+
+class QStyle;
+class QToolBar;
+class QToolButton;
+class MainWindow;
+class WebView;
+class NavigationBar : public QObject
+{
+ Q_OBJECT
+public:
+ explicit NavigationBar(MainWindow *parent = nullptr);
+
+ void addWidgetsTo(QToolBar *toolBar);
+ void connectWebView(WebView *view);
+
+private slots:
+ void update_loadStarted();
+ void update_loadFinished();
+
+private:
+ QStyle *qStyle;
+ WebView *m_view;
+ QToolButton *backButton, *forwardButton;
+ QToolButton *stopReloadButton;
+ QToolButton *homeButton;
+
+ QMetaObject::Connection loadStartedConnection, loadFinishedConnection;
+};
+
+#endif //NAVIGATIONBAR_H