aboutsummaryrefslogtreecommitdiff
path: root/src/mainwindow
diff options
context:
space:
mode:
authorAqua-sama <aqua@iserlohn-fortress.net>2018-04-16 17:07:36 +0200
committerAqua-sama <aqua@iserlohn-fortress.net>2018-04-16 17:07:36 +0200
commitd796821f8304306dbe088701724243b39e8eb358 (patch)
tree836c85aa421db5c495e2b36f6a02cea924b6d919 /src/mainwindow
parentBranch of Qt5.11 features (diff)
downloadsmolbote-d796821f8304306dbe088701724243b39e8eb358.tar.xz
Multiple subwindows interface
Subwindows are similar to tab groups. - Rewrote Browser and MainWindow, so they should be somewhat cleaner now - Moved AboutDialog to lib/about What's broken: - loading bar - search box - address bar bookmark suggestions - plugins
Diffstat (limited to 'src/mainwindow')
-rw-r--r--src/mainwindow/mainwindow.cpp362
-rw-r--r--src/mainwindow/mainwindow.h84
-rw-r--r--src/mainwindow/mainwindow.ui63
-rw-r--r--src/mainwindow/widgets/loadingbar.cpp2
-rw-r--r--src/mainwindow/widgets/navigationbar.cpp115
-rw-r--r--src/mainwindow/widgets/navigationbar.h41
-rw-r--r--src/mainwindow/widgets/searchform.cpp4
-rw-r--r--src/mainwindow/widgets/tabbar.cpp171
-rw-r--r--src/mainwindow/widgets/tabbar.h56
-rw-r--r--src/mainwindow/widgets/tabwidget.cpp121
-rw-r--r--src/mainwindow/widgets/tabwidget.h40
-rw-r--r--src/mainwindow/window.cpp68
-rw-r--r--src/mainwindow/window.h42
13 files changed, 481 insertions, 688 deletions
diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp
index 99a80b8..c039c8e 100644
--- a/src/mainwindow/mainwindow.cpp
+++ b/src/mainwindow/mainwindow.cpp
@@ -7,301 +7,169 @@
*/
#include "mainwindow.h"
-#include "addressbar/urllineedit.h"
-#include "configuration/configuration.h"
-#include "forms/aboutdialog.h"
-#include "mainwindow/widgets/searchform.h"
-#include "ui_mainwindow.h"
-#include "widgets/mainwindowmenubar.h"
-#include "mainwindow/widgets/tabbar.h"
+#include "webengine/webview.h"
+#include "widgets/navigationbar.h"
+#include "window.h"
+#include <QApplication>
+#include <QCloseEvent>
#include <QDockWidget>
+#include <QLineEdit>
+#include <QMdiArea>
+#include <QMdiSubWindow>
+#include <QMenuBar>
#include <QMessageBox>
-#include <bookmarks/bookmarksview.h>
-#include <bookmarks/bookmarkswidget.h>
-#include <downloads/downloadswidget.h>
+#include <QShortcut>
+#include <QToolBar>
+#include <QUrl>
+#include <about/aboutdialog.h>
+#include <configuration/configuration.h>
-MainWindow::MainWindow(std::shared_ptr<Configuration> config, QWidget *parent)
+MainWindow::MainWindow(std::shared_ptr<Configuration> &config, QWidget *parent)
: QMainWindow(parent)
- , ui(new Ui::MainWindow)
- , tabBar(new TabBar(config->section("tabbar"), this))
- , m_addressBar(new UrlLineEdit(config->section("addressbar"), this))
- , m_progressBar(new LoadingBar(this))
- , menuBar(new MainWindowMenuBar(config, this))
+ , mdiArea(new QMdiArea(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);
+ // create UI
+ resize(config->value<int>("mainwindow.width").value(), config->value<int>("mainwindow.height").value());
+ titleSuffix = QString::fromStdString(config->value<std::string>("mainwindow.title").value());
+ setWindowTitle(tr("smolbote"));
+ if(config->value<bool>("mainwindow.maximized").value())
+ showMaximized();
+ else
+ show();
+
+ createMenuBar();
+ auto *navigationToolBar = new NavigationBar(config->section("navigation"), this);
+ navigationToolBar->setMovable(config->value<bool>("navigation.movable").value());
+ addToolBar(Qt::TopToolBarArea, navigationToolBar);
+ navigationToolBar->connectWebView(nullptr);
+
+ setCentralWidget(mdiArea);
+ mdiArea->setFocus();
+
+ connect(mdiArea, &QMdiArea::subWindowActivated, this, [this, navigationToolBar](QMdiSubWindow *window) {
+ disconnect(titleChangedConnection);
+ disconnect(navigationBarConnection);
+
+ auto *w = qobject_cast<Window *>(window);
+ if(w != nullptr) {
+ setWindowTitle(w->windowTitle() + titleSuffix);
+ titleChangedConnection = connect(w, &Window::windowTitleChanged, this, [this](const QString &title) {
+ this->setWindowTitle(title + titleSuffix);
+ });
- // 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));
+ navigationToolBar->connectWebView(w->currentView());
+ navigationBarConnection = connect(w, &Window::currentViewChanged, navigationToolBar, &NavigationBar::connectWebView);
+ }
});
- connect(tabBar, &TabBar::currentTabChanged, this, &MainWindow::handleTabChanged);
- // loading bar
- ui->statusBar->addPermanentWidget(m_progressBar);
-
- // search box
- m_searchBox = new SearchForm(this);
- ui->statusBar->addWidget(m_searchBox);
- m_searchBox->setVisible(false);
-
- 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();
- }
+ auto *tileShortcut = new QShortcut(QKeySequence(config->value<std::string>("mainwindow.shortcuts.tileWindows").value().c_str()), this);
+ connect(tileShortcut, &QShortcut::activated, this, [this]() {
+ mdiArea->tileSubWindows();
+ });
}
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;
+ disconnect(titleChangedConnection);
+ disconnect(navigationBarConnection);
}
-void MainWindow::addTabbedDock(Qt::DockWidgetArea area, QWidget *widget)
+void MainWindow::createMenuBar()
{
- // 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;
- }
+ auto *smolboteMenu = menuBar()->addMenu(qApp->applicationDisplayName());
+ smolboteMenu->addAction(tr("New tab"), this, [this]() {
+ createTab(QUrl::fromUserInput("about:blank"));
+ },
+ QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.newTab").value().c_str()));
- if(dockWidgetArea(w) == area) {
- areaDockWidgets.append(w);
- }
- }
+ smolboteMenu->addAction(tr("New tab group"), this, [this]() {
+ createSubWindow(QUrl::fromUserInput("about:blank"));
+ },
+ QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.newGroup").value().c_str()));
+ smolboteMenu->addAction(tr("New window"))->setEnabled(false);
- // create a dock widget
- QDockWidget *dock = new QDockWidget(widget->windowTitle(), this);
- dock->setAttribute(Qt::WA_DeleteOnClose, true);
+ smolboteMenu->addSeparator();
- // 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);
+ smolboteMenu->addAction(tr("About"), this, [this]() {
+ auto *dlg = new AboutDialog(this);
+ dlg->exec();
+ },
+ QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.about").value().c_str()));
+ smolboteMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt);
- // 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);
- }
-}
+ smolboteMenu->addSeparator();
-void MainWindow::newTab(const QUrl &url)
-{
- if(!m_tabBarAdded) {
- m_tabBarAdded = true;
- ui->mainToolBar->addWidget(tabBar);
- }
- tabBar->addTab(createWebView(url, m_profile.get(), this));
-}
+ smolboteMenu->addAction(tr("Quit"), qApp, &QApplication::quit,
+ QKeySequence(m_config->value<std::string>("mainwindow.shortcuts.quit").value().c_str()));
-MainWindow *MainWindow::newWindow(const QUrl &url)
-{
- auto *instance = dynamic_cast<Browser *>(QApplication::instance());
- return instance->createSession(m_profile->storageName(), true, QStringList(url.toString()));
+ toolsMenu = menuBar()->addMenu(tr("Tools"));
}
-void MainWindow::closeEvent(QCloseEvent *event)
+void MainWindow::addAction(ActionLocation where, QAction *action)
{
- 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;
- }
+ switch(where) {
+ case ToolsMenu:
+ toolsMenu->addAction(action);
+ break;
+ default:
+ QMainWindow::addAction(action);
+ break;
}
- QMainWindow::closeEvent(event);
-}
-
-void MainWindow::about()
-{
- auto *dlg = new AboutDialog(this);
- dlg->exec();
}
-void MainWindow::setProfile(std::shared_ptr<WebEngineProfile> profile)
+void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget)
{
- Q_ASSERT(profile);
- m_profile = profile;
- tabBar->setProfile(profile.get());
- menuBar->profileAction()->setText(tr("Current profile: %1").arg(profile->name()));
-}
-
-WebEngineProfile *MainWindow::profile()
-{
- Q_ASSERT(m_profile);
- return m_profile.get();
-}
+ QDockWidget *dock = new QDockWidget(widget->windowTitle(), this);
+ dock->setAttribute(Qt::WA_DeleteOnClose, true);
+ dock->setWidget(widget);
-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);
+ connect(dock, &QDockWidget::visibilityChanged, [dock](bool visible) {
+ if(!visible && dock->widget()) {
+ dock->widget()->setParent(nullptr);
}
});
-}
-void MainWindow::setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget)
-{
- Q_ASSERT(widget);
- m_downloadsWidget = widget;
+ QMainWindow::addDockWidget(area, dock);
}
-void MainWindow::toggleFullscreen()
+void MainWindow::createTab(const QUrl &url)
{
- if(isFullScreen()) {
- setWindowState(Qt::WindowMaximized | Qt::WindowActive);
+ auto *w = qobject_cast<Window *>(mdiArea->currentSubWindow());
+ if(w == nullptr) {
+ w = createSubWindow(url);
} else {
- setWindowState(Qt::WindowFullScreen | Qt::WindowActive);
+ w->addTab(url);
}
}
-void MainWindow::handleTabChanged(WebView *view)
+Window *MainWindow::createSubWindow(const QUrl &url)
{
- Q_CHECK_PTR(view);
+ auto *w = new Window(this);
+ mdiArea->addSubWindow(w);
+ w->showMaximized();
+ w->setFocus();
- disconnect(titleChangedConnection);
- disconnect(newBookmarkConnection);
+ w->addTab(url);
- 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);
-
- m_addressBar->connectWebView(view);
-
- titleChangedConnection = connect(view, &WebView::titleChanged, this, &MainWindow::handleTitleUpdated);
- newBookmarkConnection = connect(view, &WebView::newBookmark, this, [this](const QString &title, const QUrl &url) {
- if(m_bookmarksWidget) {
- auto *bookmark = m_bookmarksWidget->model()->createBookmark(nullptr);
- bookmark->setText(0, title);
- bookmark->setText(1, url.toString());
- }
- });
-
- m_progressBar->connectWebView(view);
-
- // update UI
- this->handleTitleUpdated(view->title());
- centralWidget()->setFocus();
+ return w;
}
-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)
+void MainWindow::closeEvent(QCloseEvent *event)
{
- for(const Browser::Plugin &plugin : plugins) {
- ProfileInterface *iProfilePlugin = qobject_cast<ProfileInterface *>(plugin.pointer);
- if(iProfilePlugin) {
- QWidget *w = iProfilePlugin->createWidget(m_profile.get(), this);
-
- menuBar->profileAction()->setEnabled(true);
- connect(menuBar->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();
- }
- });
+ if(mdiArea->subWindowList().count() > 1) {
+ int choice = QMessageBox::question(this, tr("Close multiple subwindows?"), tr("Do you want to close all subwindows?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
+ if(choice == QMessageBox::No) {
+ event->ignore();
+ return;
}
}
+
+ mdiArea->closeAllSubWindows();
+ if(mdiArea->currentSubWindow())
+ event->ignore();
+ else
+ event->accept();
}
diff --git a/src/mainwindow/mainwindow.h b/src/mainwindow/mainwindow.h
index 8b205ab..1817fdd 100644
--- a/src/mainwindow/mainwindow.h
+++ b/src/mainwindow/mainwindow.h
@@ -6,93 +6,49 @@
* SPDX-License-Identifier: GPL-3.0
*/
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
+#ifndef SMOLBOTE_MAINWINDOW_H
+#define SMOLBOTE_MAINWINDOW_H
-#include "browser.h"
-#include "webengine/webengineprofile.h"
-#include "widgets/loadingbar.h"
-#include "widgets/navigationbar.h"
#include <QMainWindow>
-#include <QUrl>
-#include <interfaces.h>
#include <memory>
-namespace Ui
-{
-class MainWindow;
-}
-
-class TabBar;
-class SearchForm;
+class QMdiArea;
class Configuration;
-class BookmarksWidget;
-class DownloadsWidget;
-class MainWindowMenuBar;
-class UrlLineEdit;
-
+class Window;
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);
+ enum ActionLocation {
+ ToolsMenu
+ };
+
+ 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 newTab(const QUrl &url = QUrl(""));
- MainWindow *newWindow(const QUrl &url = QUrl(""));
+ void createMenuBar();
- void setProfile(std::shared_ptr<WebEngineProfile> profile);
- WebEngineProfile *profile();
+ void addAction(ActionLocation where, QAction *action);
+ void addDockWidget(Qt::DockWidgetArea area, QWidget *widget);
- void setBookmarksWidget(std::shared_ptr<BookmarksWidget> &widget);
- void setDownloadsWidget(std::shared_ptr<DownloadsWidget> &widget);
- void addPlugins(const QVector<Browser::Plugin> &plugins);
-
- void toggleFullscreen();
+public slots:
+ void createTab(const QUrl &url);
+ Window *createSubWindow(const QUrl &url);
protected:
void closeEvent(QCloseEvent *event) override;
-private slots:
- void handleTabChanged(WebView *view);
- void handleTitleUpdated(const QString &title);
-
private:
- Ui::MainWindow *ui;
- SearchForm *m_searchBox;
-
- TabBar *tabBar;
- WebView *m_currentView;
-
- MainWindowMenuBar *menuBar;
-
- // navigation
- NavigationBar *m_navigationBar;
- UrlLineEdit *m_addressBar;
- LoadingBar *m_progressBar;
+ QString titleSuffix;
+ QMenu *toolsMenu = nullptr;
+ QMdiArea *mdiArea;
- 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;
QMetaObject::Connection titleChangedConnection;
- QMetaObject::Connection newBookmarkConnection;
+ QMetaObject::Connection navigationBarConnection;
};
-#endif // MAINWINDOW_H
+#endif // SMOLBOTE_MAINWINDOW_H
diff --git a/src/mainwindow/mainwindow.ui b/src/mainwindow/mainwindow.ui
deleted file mode 100644
index 12a3a6c..0000000
--- a/src/mainwindow/mainwindow.ui
+++ /dev/null
@@ -1,63 +0,0 @@
-<?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
index 76eafc4..61e3eae 100644
--- a/src/mainwindow/widgets/loadingbar.cpp
+++ b/src/mainwindow/widgets/loadingbar.cpp
@@ -7,8 +7,8 @@
*/
#include "loadingbar.h"
-#include <QTimer>
#include "webengine/webview.h"
+#include <QTimer>
LoadingBar::LoadingBar(QWidget *parent)
: QProgressBar(parent)
diff --git a/src/mainwindow/widgets/navigationbar.cpp b/src/mainwindow/widgets/navigationbar.cpp
index 0652159..e2c714e 100644
--- a/src/mainwindow/widgets/navigationbar.cpp
+++ b/src/mainwindow/widgets/navigationbar.cpp
@@ -7,7 +7,9 @@
*/
#include "navigationbar.h"
-#include "mainwindow/mainwindow.h"
+#include "addressbar/urllineedit.h"
+#include "configuration/configuration.h"
+#include "webengine/webview.h"
#include <QHBoxLayout>
#include <QMenu>
#include <QShortcut>
@@ -15,99 +17,94 @@
#include <QToolBar>
#include <QToolButton>
#include <QWebEngineHistory>
-#include "webengine/webview.h"
-#include "configuration/configuration.h"
-NavigationBar::NavigationBar(MainWindow *parent)
- : QObject(parent)
+NavigationBar::NavigationBar(const QHash<QString, QString> &conf, QWidget *parent)
+ : QToolBar(parent)
{
qStyle = parent->style();
// Back button
- backButton = new QToolButton(parent);
- backButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowBack));
- backButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.back").value()));
- connect(backButton, &QToolButton::clicked, this, [this]() {
+ backAction = addAction(qStyle->standardIcon(QStyle::SP_ArrowBack), tr("Back"));
+ backAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.back")));
+ connect(backAction, &QAction::triggered, this, [this]() {
m_view->history()->back();
});
- auto *backMenu = new QMenu(backButton);
- backButton->setMenu(backMenu);
+ auto *backMenu = new QMenu(this);
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);
+ for(const QWebEngineHistoryItem &item : m_view->history()->backItems(10)) {
+ auto *action = backMenu->addAction(item.title());
+ connect(action, &QAction::triggered, this, [item, this]() {
+ m_view->history()->goToItem(item);
});
}
});
+ backAction->setMenu(backMenu);
// Forward button
- forwardButton = new QToolButton(parent);
- forwardButton->setIcon(qStyle->standardIcon(QStyle::SP_ArrowForward));
- forwardButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.forward").value()));
- connect(forwardButton, &QToolButton::clicked, this, [this]() {
+ forwardAction = addAction(qStyle->standardIcon(QStyle::SP_ArrowForward), tr("Forward"));
+ forwardAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.forward")));
+ connect(forwardAction, &QAction::triggered, this, [this]() {
m_view->history()->forward();
});
- auto *forwardMenu = new QMenu(forwardButton);
- forwardButton->setMenu(forwardMenu);
+ auto *forwardMenu = new QMenu(this);
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);
+ for(const QWebEngineHistoryItem &item : m_view->history()->forwardItems(10)) {
+ auto *action = forwardMenu->addAction(item.title());
+ connect(action, &QAction::triggered, this, [item, this]() {
+ m_view->history()->goToItem(item);
});
}
});
+ forwardAction->setMenu(forwardMenu);
// Stop/Refresh button
- stopReloadButton = new QToolButton(parent);
- stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload));
- stopReloadButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.refresh").value()));
- connect(stopReloadButton, &QToolButton::clicked, this, [this]() {
+ stopReloadAction = addAction(qStyle->standardIcon(QStyle::SP_BrowserReload), tr("Reload"));
+ stopReloadAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.refresh")));
+ connect(stopReloadAction, &QAction::triggered, this, [this]() {
if(m_view->isLoaded())
m_view->reload();
else
m_view->stop();
});
- auto *reloadShortcut = new QShortcut(
- QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.reload").value()),
- parent);
- connect(reloadShortcut, &QShortcut::activated, this, [this]() {
- m_view->reload();
- });
-
// Home button
- homeButton = new QToolButton(parent);
- homeButton->setIcon(qStyle->standardIcon(QStyle::SP_DirHomeIcon));
- homeButton->setShortcut(QString::fromStdString(parent->m_config->value<std::string>("browser.shortcuts.home").value()));
- connect(homeButton, &QToolButton::clicked, this, [this, parent]() {
- m_view->load(parent->m_profile->homepage());
+ homeAction = addAction(qStyle->standardIcon(QStyle::SP_DirHomeIcon), tr("Home"));
+ homeAction->setShortcut(QKeySequence(conf.value("navigation.shortcuts.home")));
+ connect(homeAction, &QAction::triggered, this, [this]() {
+ m_view->triggerViewAction(WebView::GoHome);
});
-}
-void NavigationBar::addWidgetsTo(QToolBar *toolBar)
-{
- toolBar->addWidget(backButton);
- toolBar->addWidget(forwardButton);
- toolBar->addWidget(stopReloadButton);
- toolBar->addWidget(homeButton);
+ QHash<QString, QString> a;
+ addressBar = new UrlLineEdit(a, this);
+ addWidget(addressBar);
+
+ auto *focusShortcut = new QShortcut(QKeySequence("F4"), this);
+ connect(focusShortcut, &QShortcut::activated, this, [this]() {
+ addressBar->setFocus();
+ addressBar->selectAll();
+ });
}
void NavigationBar::connectWebView(WebView *view)
{
- Q_CHECK_PTR(view);
m_view = view;
disconnect(loadStartedConnection);
disconnect(loadFinishedConnection);
+ if(view == nullptr) {
+ backAction->setEnabled(false);
+ forwardAction->setEnabled(false);
+ stopReloadAction->setEnabled(false);
+ homeAction->setEnabled(false);
+
+ return;
+ }
+
if(view->isLoaded()) {
update_loadFinished();
} else {
@@ -116,18 +113,22 @@ void NavigationBar::connectWebView(WebView *view)
loadStartedConnection = connect(view, &QWebEngineView::loadStarted, this, &NavigationBar::update_loadStarted);
loadFinishedConnection = connect(view, &WebView::loaded, this, &NavigationBar::update_loadFinished);
+ stopReloadAction->setEnabled(true);
+ homeAction->setEnabled(true);
+
+ addressBar->connectWebView(view);
}
void NavigationBar::update_loadStarted()
{
- backButton->setEnabled(m_view->history()->canGoForward());
- forwardButton->setEnabled(m_view->history()->canGoForward());
- stopReloadButton->setIcon(qStyle->standardIcon(QStyle::SP_BrowserStop));
+ backAction->setEnabled(m_view->history()->canGoForward());
+ forwardAction->setEnabled(m_view->history()->canGoForward());
+ stopReloadAction->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));
+ backAction->setEnabled(m_view->history()->canGoBack());
+ forwardAction->setEnabled(m_view->history()->canGoForward());
+ stopReloadAction->setIcon(qStyle->standardIcon(QStyle::SP_BrowserReload));
}
diff --git a/src/mainwindow/widgets/navigationbar.h b/src/mainwindow/widgets/navigationbar.h
index fd0579d..1b4cc05 100644
--- a/src/mainwindow/widgets/navigationbar.h
+++ b/src/mainwindow/widgets/navigationbar.h
@@ -6,36 +6,21 @@
* 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
+#ifndef SMOLBOTE_NAVIGATIONBAR_H
+#define SMOLBOTE_NAVIGATIONBAR_H
-#include <QObject>
+#include <QToolBar>
-class QStyle;
-class QToolBar;
-class QToolButton;
-class MainWindow;
+class UrlLineEdit;
class WebView;
-class NavigationBar : public QObject
+class NavigationBar : public QToolBar
{
Q_OBJECT
+
public:
- explicit NavigationBar(MainWindow *parent = nullptr);
+ explicit NavigationBar(const QHash<QString, QString> &conf, QWidget *parent = nullptr);
- void addWidgetsTo(QToolBar *toolBar);
+public slots:
void connectWebView(WebView *view);
private slots:
@@ -45,11 +30,13 @@ private slots:
private:
QStyle *qStyle;
WebView *m_view;
- QToolButton *backButton, *forwardButton;
- QToolButton *stopReloadButton;
- QToolButton *homeButton;
+
+ QAction *backAction, *forwardAction;
+ QAction *stopReloadAction;
+ QAction *homeAction;
+ UrlLineEdit *addressBar;
QMetaObject::Connection loadStartedConnection, loadFinishedConnection;
};
-#endif //NAVIGATIONBAR_H
+#endif // SMOLBOTE_NAVIGATIONBAR_H
diff --git a/src/mainwindow/widgets/searchform.cpp b/src/mainwindow/widgets/searchform.cpp
index 8ac1bc2..07b8426 100644
--- a/src/mainwindow/widgets/searchform.cpp
+++ b/src/mainwindow/widgets/searchform.cpp
@@ -7,11 +7,11 @@
*/
#include "searchform.h"
+#include "configuration/configuration.h"
#include "mainwindow/mainwindow.h"
#include "ui_searchform.h"
-#include <QAction>
#include "webengine/webview.h"
-#include "configuration/configuration.h"
+#include <QAction>
SearchForm::SearchForm(MainWindow *parentWindow, QWidget *parent)
: QWidget(parent)
diff --git a/src/mainwindow/widgets/tabbar.cpp b/src/mainwindow/widgets/tabbar.cpp
deleted file mode 100644
index ef32ef0..0000000
--- a/src/mainwindow/widgets/tabbar.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#include "tabbar.h"
-#include "mainwindow/mainwindow.h"
-#include "webengine/webengineprofile.h"
-#include "webengine/webview.h"
-#include <QContextMenuEvent>
-#include <QMenu>
-
-TabBar::TabBar(const QHash<QString, QString> &config, MainWindow *parent)
- : QTabBar(parent)
-{
- Q_CHECK_PTR(parent);
-
- setElideMode(Qt::ElideRight);
- setTabsClosable(true);
- setMovable(true);
- setContextMenuPolicy(Qt::DefaultContextMenu);
-
- connect(this, &TabBar::tabCloseRequested, this, &TabBar::removeTab);
- connect(this, &TabBar::currentChanged, this, &TabBar::handleCurrentChanged);
- connect(this, &TabBar::tabMoved, this, &TabBar::updateVectorArrangement);
-
- newTab_action = new QAction(tr("New Tab"), parent);
- newTab_action->setObjectName("newTab_action");
- newTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.new"]));
- connect(newTab_action, &QAction::triggered, parent, [parent, this]() {
- parent->newTab(parent->profile()->newtab());
- });
- parent->addAction(newTab_action);
-
- closeTab_action = new QAction(tr("Close Tab"), parent);
- closeTab_action->setObjectName("closeTab_action");
- closeTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.close"]));
- connect(closeTab_action, &QAction::triggered, this, [this]() {
- removeTab(currentIndex());
- });
- parent->addAction(closeTab_action);
-
- leftTab_action = new QAction(tr("Left Tab"), parent);
- leftTab_action->setObjectName("leftTab_action");
- leftTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.left"]));
- connect(leftTab_action, &QAction::triggered, this, [this]() {
- setCurrentIndex(currentIndex() - 1);
- });
- parent->addAction(leftTab_action);
-
- rightTab_action = new QAction(tr("Right Tab"), parent);
- rightTab_action->setObjectName("rightTab_action");
- rightTab_action->setShortcut(QKeySequence(config["tabbar.shortcuts.right"]));
- connect(rightTab_action, &QAction::triggered, this, [this]() {
- setCurrentIndex(currentIndex() + 1);
- });
- parent->addAction(rightTab_action);
-
- // tab context menu
- {
- tabContextMenu = new QMenu(this);
-
- auto *closeTab = tabContextMenu->addAction(tr("Close Tab"));
- connect(closeTab, &QAction::triggered, this, [this]() {
- removeTab(tabAt(mapFromGlobal(tabContextMenu->pos())));
- });
-
- auto *closeTabsLeft = tabContextMenu->addAction(tr("Close Tabs left"));
- connect(closeTabsLeft, &QAction::triggered, this, [this]() {
- int idx = tabAt(mapFromGlobal(tabContextMenu->pos()));
- for(int i = idx - 1; i >= 0; --i) {
- removeTab(i);
- }
- });
-
- auto *closeTabsRight = tabContextMenu->addAction(tr("Close Tabs right"));
- connect(closeTabsRight, &QAction::triggered, this, [this]() {
- int idx = tabAt(mapFromGlobal(tabContextMenu->pos()));
- for(int i = count() - 1; i > idx; --i) {
- removeTab(i);
- }
- });
- }
-}
-
-TabBar::~TabBar()
-{
- // cleanup
- qDeleteAll(m_views);
- m_views.clear();
-}
-
-void TabBar::setProfile(WebEngineProfile *profile)
-{
- Q_CHECK_PTR(profile);
-
- for(auto view : qAsConst(m_views)) {
- auto *page = new WebPage(profile);
- page->load(view->url());
- view->page()->deleteLater();
- view->setPage(page);
- }
-}
-
-WebView *TabBar::currentView()
-{
- return m_views.at(currentIndex());
-}
-
-int TabBar::addTab(WebView *view)
-{
- m_views.append(view);
-
- connect(view, &QWebEngineView::titleChanged, [this, view](const QString &title) {
- int index = m_views.indexOf(view);
- setTabText(index, title);
- setTabToolTip(index, title);
- });
- connect(view, &QWebEngineView::iconChanged, [this, view](const QIcon &icon) {
- int index = m_views.indexOf(view);
- setTabIcon(index, icon);
- });
-
- return QTabBar::addTab("New Tab");
-}
-
-void TabBar::removeTab(int index)
-{
- // remove the tab data from the index
- m_views.at(index)->deleteLater();
- m_views.remove(index);
-
- // remove the tab from the QTabBar
- // this emits the currentTabChanged signal, so it should be done after the view is removed from the index
- QTabBar::removeTab(index);
-}
-
-void TabBar::contextMenuEvent(QContextMenuEvent *event)
-{
- // check if the context menu was called on a tab
- int tabIndex = tabAt(event->pos());
- if(tabIndex < 0) {
- return;
- }
-
- tabContextMenu->exec(event->globalPos());
-}
-
-QSize TabBar::tabSizeHint(int index) const
-{
- Q_UNUSED(index)
- return QSize(200, this->height());
-}
-
-void TabBar::handleCurrentChanged(int index)
-{
- if(index < 0) {
- newTab_action->trigger();
- return;
- }
-
- emit currentTabChanged(m_views.at(index));
-}
-
-void TabBar::updateVectorArrangement(int from, int to)
-{
- m_views.move(from, to);
-}
diff --git a/src/mainwindow/widgets/tabbar.h b/src/mainwindow/widgets/tabbar.h
deleted file mode 100644
index f4b7414..0000000
--- a/src/mainwindow/widgets/tabbar.h
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#ifndef SMOLBOTE_TABBAR_H
-#define SMOLBOTE_TABBAR_H
-
-#include <QTabBar>
-
-class MainWindow;
-class WebView;
-class WebEngineProfile;
-class QMenu;
-class TabBar : public QTabBar
-{
- Q_OBJECT
-
-public:
- explicit TabBar(const QHash<QString, QString> &config, MainWindow *parent = nullptr);
- ~TabBar() override;
-
- void setProfile(WebEngineProfile *profile);
- WebView *currentView();
-
-signals:
- void currentTabChanged(WebView *view);
-
-public slots:
- int addTab(WebView *view);
- void removeTab(int index);
-
-protected:
- void contextMenuEvent(QContextMenuEvent *event) override;
- QSize tabSizeHint(int index) const override;
-
-private slots:
- void handleCurrentChanged(int index);
- void updateVectorArrangement(int from, int to);
-
-private:
- // store all views in a vector since tabs can only store a QVariant, and that can't easily take a pointer
- QVector<WebView *> m_views;
-
- QMenu *tabContextMenu;
-
- QAction *newTab_action;
- QAction *closeTab_action;
- QAction *leftTab_action;
- QAction *rightTab_action;
-};
-
-#endif // SMOLBOTE_TABBAR_H
diff --git a/src/mainwindow/widgets/tabwidget.cpp b/src/mainwindow/widgets/tabwidget.cpp
new file mode 100644
index 0000000..21476cd
--- /dev/null
+++ b/src/mainwindow/widgets/tabwidget.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "tabwidget.h"
+#include "webengine/webview.h"
+#include <QAction>
+#include <QContextMenuEvent>
+#include <QMenu>
+#include <QTabBar>
+
+TabWidget::TabWidget(QWidget *parent)
+ : QTabWidget(parent)
+{
+ setStyleSheet("QTabBar::tab { width: 200px; }");
+
+ setTabsClosable(true);
+ setTabBarAutoHide(true);
+ setElideMode(Qt::ElideRight);
+ setMovable(true);
+
+ connect(this, &TabWidget::tabCloseRequested, this, &TabWidget::deleteTab);
+
+ // general actions
+ closeTab_action = new QAction(this);
+ closeTab_action->setShortcut(QKeySequence("Ctrl+X"));
+ connect(closeTab_action, &QAction::triggered, this, [this]() {
+ this->deleteTab(this->currentIndex());
+ });
+ addAction(closeTab_action);
+
+ leftTab_action = new QAction(this);
+ leftTab_action->setShortcut(QKeySequence("Ctrl+O"));
+ connect(leftTab_action, &QAction::triggered, this, [this]() {
+ this->setCurrentIndex(qMax(0, this->currentIndex() - 1));
+ });
+ addAction(leftTab_action);
+
+ rightTab_action = new QAction(this);
+ rightTab_action->setShortcut(QKeySequence("Ctrl+P"));
+ connect(rightTab_action, &QAction::triggered, this, [this]() {
+ this->setCurrentIndex(qMin(this->currentIndex() + 1, this->count() - 1));
+ });
+ addAction(rightTab_action);
+
+ // context menu
+ tabContextMenu = new QMenu(this);
+ auto *closeTab = tabContextMenu->addAction(tr("Close Tab"));
+ connect(closeTab, &QAction::triggered, this, [this]() {
+ deleteTab(this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos())));
+ });
+
+ auto *closeTabsLeft = tabContextMenu->addAction(tr("Close Tabs left"));
+ connect(closeTabsLeft, &QAction::triggered, this, [this]() {
+ int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos()));
+ for(int i = idx - 1; i >= 0; --i) {
+ deleteTab(i);
+ }
+ });
+
+ auto *closeTabsRight = tabContextMenu->addAction(tr("Close Tabs right"));
+ connect(closeTabsRight, &QAction::triggered, this, [this]() {
+ int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos()));
+ for(int i = count() - 1; i > idx; --i) {
+ deleteTab(i);
+ }
+ });
+}
+
+TabWidget::~TabWidget()
+{
+ for(int i = count() - 1; i >= 0; i--) {
+ delete widget(i);
+ }
+}
+
+int TabWidget::addTab(WebView *view)
+{
+ Q_CHECK_PTR(view);
+
+ int idx = QTabWidget::addTab(view, view->title());
+ connect(view, &WebView::titleChanged, [this, view](const QString &title) {
+ int idx = this->indexOf(view);
+ Q_ASSERT(idx != -1);
+
+ this->setTabText(idx, title);
+ });
+ connect(view, &WebView::iconChanged, [this, view](const QIcon &icon) {
+ int idx = this->indexOf(view);
+ Q_ASSERT(idx != -1);
+
+ this->setTabIcon(idx, icon);
+ });
+
+ return idx;
+}
+
+void TabWidget::deleteTab(int index)
+{
+ // deleting the widget automatically removes the tab?
+ if(count() > 1) {
+ widget(index)->deleteLater();
+ removeTab(index);
+ } else
+ parentWidget()->close();
+}
+
+void TabWidget::contextMenuEvent(QContextMenuEvent *event)
+{
+ // check if the context menu was called on a tab
+ int tabIndex = tabBar()->tabAt(event->pos());
+ if(tabIndex < 0) {
+ return;
+ }
+
+ tabContextMenu->exec(event->globalPos());
+}
diff --git a/src/mainwindow/widgets/tabwidget.h b/src/mainwindow/widgets/tabwidget.h
new file mode 100644
index 0000000..b7aad63
--- /dev/null
+++ b/src/mainwindow/widgets/tabwidget.h
@@ -0,0 +1,40 @@
+/*
+ * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef SMOLBOTE_TABWIDGET_H
+#define SMOLBOTE_TABWIDGET_H
+
+#include <QTabWidget>
+
+class QAction;
+class QMenu;
+class WebView;
+class TabWidget : public QTabWidget
+{
+ Q_OBJECT
+
+public:
+ explicit TabWidget(QWidget *parent = nullptr);
+ ~TabWidget() override;
+
+public slots:
+ int addTab(WebView *view);
+ void deleteTab(int index);
+
+protected:
+ void contextMenuEvent(QContextMenuEvent *event) override;
+
+private:
+ QAction *closeTab_action;
+ QAction *leftTab_action;
+ QAction *rightTab_action;
+
+ QMenu *tabContextMenu;
+};
+
+#endif // SMOLBOTE_TABWIDGET_H
diff --git a/src/mainwindow/window.cpp b/src/mainwindow/window.cpp
new file mode 100644
index 0000000..67397fe
--- /dev/null
+++ b/src/mainwindow/window.cpp
@@ -0,0 +1,68 @@
+/*
+ * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "window.h"
+#include "webengine/webprofile.h"
+#include "webengine/webview.h"
+#include "widgets/tabwidget.h"
+#include <QUrl>
+
+Window::Window(QWidget *parent, Qt::WindowFlags flags)
+ : QMdiSubWindow(parent, flags)
+ , tabWidget(new TabWidget(this))
+{
+ // delete this window when it closes
+ setAttribute(Qt::WA_DeleteOnClose, true);
+
+ resize(800, 600);
+ setWidget(tabWidget);
+
+ connect(tabWidget, &TabWidget::currentChanged, [this](int index) {
+ if(index < 0) {
+ // last tab has been closed
+ close();
+ } else {
+ auto *view = dynamic_cast<WebView *>(tabWidget->widget(index));
+ Q_CHECK_PTR(view);
+
+ disconnect(titleConnection);
+ connect(view, &WebView::titleChanged, this, &Window::setWindowTitle);
+ setWindowTitle(view->title());
+
+ emit currentViewChanged(view);
+ }
+ });
+}
+
+Window::~Window()
+{
+ delete tabWidget;
+}
+
+WebView *Window::currentView()
+{
+ return qobject_cast<WebView *>(tabWidget->currentWidget());
+}
+
+int Window::addTab(WebView *view)
+{
+ Q_CHECK_PTR(view);
+ return tabWidget->addTab(view);
+}
+
+int Window::addTab(const QUrl &url)
+{
+ auto *view = new WebView(WebProfile::defaultProfile(), this);
+ view->load(url);
+ return tabWidget->addTab(view);
+}
+
+void Window::swapToTab(int index)
+{
+ tabWidget->setCurrentIndex(index);
+}
diff --git a/src/mainwindow/window.h b/src/mainwindow/window.h
new file mode 100644
index 0000000..743d75f
--- /dev/null
+++ b/src/mainwindow/window.h
@@ -0,0 +1,42 @@
+/*
+ * 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: https://neueland.iserlohn-fortress.net/smolbote.hg
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef SMOLBOTE_WINDOW_H
+#define SMOLBOTE_WINDOW_H
+
+#include <QMdiSubWindow>
+#include <memory>
+
+class TabWidget;
+class WebView;
+class Window : public QMdiSubWindow
+{
+ Q_OBJECT
+
+public:
+ explicit Window(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
+ ~Window() override;
+
+ WebView *currentView();
+
+signals:
+ void currentViewChanged(WebView *view);
+
+public slots:
+ int addTab(WebView *view);
+ int addTab(const QUrl &url);
+
+ void swapToTab(int index);
+
+private:
+ TabWidget *tabWidget;
+
+ QMetaObject::Connection titleConnection;
+};
+
+#endif // SMOLBOTE_WINDOW_H