diff options
Diffstat (limited to 'src')
35 files changed, 932 insertions, 1562 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0f978b5..8adb1aa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,16 +20,17 @@ add_executable(poi # main window mainwindow/mainwindow.cpp mainwindow/mainwindow.h - mainwindow/mainwindow.ui - mainwindow/widgets/loadingbar.cpp - mainwindow/widgets/loadingbar.h + mainwindow/window.cpp + mainwindow/window.h + #mainwindow/widgets/loadingbar.cpp + #mainwindow/widgets/loadingbar.h mainwindow/widgets/navigationbar.cpp mainwindow/widgets/navigationbar.h - mainwindow/widgets/searchform.cpp - mainwindow/widgets/searchform.h - mainwindow/widgets/searchform.ui - mainwindow/widgets/tabbar.cpp - mainwindow/widgets/tabbar.h + #mainwindow/widgets/searchform.cpp + #mainwindow/widgets/searchform.h + #mainwindow/widgets/searchform.ui + mainwindow/widgets/tabwidget.cpp + mainwindow/widgets/tabwidget.h # address bar addressbar/completer.cpp @@ -37,15 +38,11 @@ add_executable(poi addressbar/urllineedit.cpp addressbar/urllineedit.h - # todo: move all to mainwindow - widgets/mainwindowmenubar.cpp - widgets/mainwindowmenubar.h - # webengine webengine/urlinterceptor.cpp webengine/urlinterceptor.h - webengine/webengineprofile.cpp # todo: rename to profile - webengine/webengineprofile.h + webengine/webprofile.cpp + webengine/webprofile.h webengine/webpage.cpp webengine/webpage.h webengine/webview.cpp @@ -55,13 +52,8 @@ add_executable(poi webengine/widgets/pagemenu.cpp webengine/widgets/pagemenu.h - # forms - forms/aboutdialog.cpp - forms/aboutdialog.h - forms/aboutdialog.ui - # plugin interfaces - ../plugins/interfaces.h + #../plugins/interfaces.h ) target_include_directories(poi @@ -70,6 +62,7 @@ target_include_directories(poi target_link_libraries(poi Qt5::Core Qt5::Widgets Qt5::Concurrent Qt5::WebEngineWidgets + about configuration bookmarks downloads) diff --git a/src/addressbar/completer.h b/src/addressbar/completer.h index ce19f07..daef67c 100644 --- a/src/addressbar/completer.h +++ b/src/addressbar/completer.h @@ -21,14 +21,12 @@ class Completer : public QListView public: explicit Completer(QWidget *parent = nullptr); - bool updateItems(const QList<QTreeWidgetItem*> &list); + bool updateItems(const QList<QTreeWidgetItem *> &list); bool keyPressed(QKeyEvent *event); private: QStringListModel *completionModel; - }; - #endif //SMOLBOTE_COMPLETER_H diff --git a/src/addressbar/urllineedit.cpp b/src/addressbar/urllineedit.cpp index cf2c08b..8aa03c7 100644 --- a/src/addressbar/urllineedit.cpp +++ b/src/addressbar/urllineedit.cpp @@ -7,6 +7,7 @@ */ #include "urllineedit.h" +#include "webengine/webview.h" #include <QLabel> #include <QMenu> #include <QShortcut> @@ -18,19 +19,13 @@ UrlLineEdit::UrlLineEdit(const QHash<QString, QString> &config, QWidget *parent) : QLineEdit(parent) , m_listView(new Completer(this)) { - auto *focusShortcut = new QShortcut(QKeySequence(config.value("addressbar.shortcuts.focus")), parent); - connect(focusShortcut, &QShortcut::activated, this, [this]() { - setFocus(); - selectAll(); - }); - setPlaceholderText(tr("Enter address")); m_listView->setVisible(false); connect(this, &UrlLineEdit::textEdited, this, &UrlLineEdit::updateCompleter); m_pageMenuAction = addAction(style()->standardIcon(QStyle::SP_DriveNetIcon), QLineEdit::LeadingPosition); - m_pageMenuAction->setShortcut(QKeySequence(config.value("addressbar.shortcuts.pageMenu"))); + m_pageMenuAction->setShortcut(QKeySequence("F2")); m_pageMenuAction->setToolTip(tr("Page Actions (%1)").arg(m_pageMenuAction->shortcut().toString())); connect(m_pageMenuAction, &QAction::triggered, m_pageMenuAction, [&]() { if(m_pageMenuAction->menu()) { @@ -39,7 +34,7 @@ UrlLineEdit::UrlLineEdit(const QHash<QString, QString> &config, QWidget *parent) }); m_toolsMenuAction = addAction(style()->standardIcon(QStyle::SP_FileIcon), QLineEdit::TrailingPosition); - m_toolsMenuAction->setShortcut(QKeySequence(config.value("addressbar.shortcuts.toolsMenu"))); + m_toolsMenuAction->setShortcut(QKeySequence("F10")); m_toolsMenuAction->setToolTip(tr("Tools (%1)").arg(m_toolsMenuAction->shortcut().toString())); connect(m_toolsMenuAction, &QAction::triggered, m_toolsMenuAction, [&]() { if(m_toolsMenuAction->menu()) { @@ -56,7 +51,11 @@ UrlLineEdit::UrlLineEdit(const QHash<QString, QString> &config, QWidget *parent) if(this->text().startsWith('#')) { emit searchTermEntered(this->text().mid(1)); } else { - emit addressEntered(QUrl::fromUserInput(this->text())); + if(m_view) { + m_view->load(QUrl::fromUserInput(this->text())); + } else { + this->clear(); + } } this->clearFocus(); }); @@ -71,6 +70,7 @@ void UrlLineEdit::setCompleterModel(BookmarksView *model) void UrlLineEdit::connectWebView(WebView *view) { Q_CHECK_PTR(view); + m_view = view; disconnect(urlChangedConnection); diff --git a/src/addressbar/urllineedit.h b/src/addressbar/urllineedit.h index 17465bc..24ad403 100644 --- a/src/addressbar/urllineedit.h +++ b/src/addressbar/urllineedit.h @@ -9,15 +9,16 @@ #ifndef SMOLBOTE_URLLINEEDIT_H #define SMOLBOTE_URLLINEEDIT_H +#include "completer.h" +#include "webengine/webview.h" #include <QAction> #include <QLineEdit> #include <QTextLayout> -#include "webengine/webview.h" -#include "completer.h" class BookmarksView; class QMenu; class QLabel; +class WebView; class UrlLineEdit : public QLineEdit { Q_OBJECT @@ -45,6 +46,8 @@ private: void setTextFormat(const QTextLayout::FormatRange &format); void clearTextFormat(); + WebView *m_view = nullptr; + QTextLayout::FormatRange m_hostFormat; // pageMenu action: zoom, print diff --git a/src/browser.cpp b/src/browser.cpp index c32aee7..ab07f2e 100644 --- a/src/browser.cpp +++ b/src/browser.cpp @@ -7,152 +7,114 @@ */ #include "browser.h" -#include "configuration/configuration.h" #include "mainwindow/mainwindow.h" -#include "webengine/urlinterceptor.h" -#include "webengine/webengineprofile.h" -#include <QWebEngineDownloadItem> -#include <QtConcurrent> +#include "mainwindow/window.h" +#include "webengine/webprofile.h" +#include <QAction> +#include <configuration/configuration.h> #include <bookmarks/bookmarkswidget.h> #include <downloads/downloadswidget.h> +#include <version.h> +#include "webengine/urlinterceptor.h" Browser::Browser(int &argc, char *argv[]) : SingleApplication(argc, argv) { - QApplication::setApplicationName("smolbote"); - QApplication::setWindowIcon(QIcon(":/icon.svg")); - - connect(this, &Browser::messageAvailable, this, &Browser::createSession); + setApplicationName("smolbote"); + setWindowIcon(QIcon(":/icon.svg")); + setApplicationVersion(SMOLBOTE_VERSION); } Browser::~Browser() { - if(m_bookmarksManager) { - QtConcurrent::run(QThreadPool::globalInstance(), m_bookmarksManager.get(), &BookmarksWidget::save); - } - - qDebug("Waiting for threads to wind down..."); - qDebug("Thread pool is done: %s", QThreadPool::globalInstance()->waitForDone() ? "okay" : "failed"); - - delete m_urlRequestInterceptor; + qDeleteAll(m_windows); + m_windows.clear(); } void Browser::setConfiguration(std::shared_ptr<Configuration> &config) { + Q_ASSERT(config); m_config = config; - - QDir pluginsDir(QString::fromStdString(m_config->value<std::string>("plugins.path").value())); - if(pluginsDir.exists()) { - const QStringList entries = pluginsDir.entryList(QDir::Files | QDir::Readable); - for(const QString &name : entries) { - QPluginLoader loader(pluginsDir.absoluteFilePath(name)); - qDebug("Loading plugin %s: %s", qUtf8Printable(name), loader.load() ? "ok" : "failed"); - if(!loader.isLoaded()) { - qDebug("Error: %s", qUtf8Printable(loader.errorString())); - } else { - Plugin d; - d.meta = loader.metaData()["MetaData"].toObject(); - d.pointer = loader.instance(); - d.pointer->setParent(this); - m_plugins.append(d); - } - } - } - - m_bookmarksManager = std::make_shared<BookmarksWidget>(QString::fromStdString(m_config->value<std::string>("bookmarks.path").value())); - m_downloadManager = std::make_shared<DownloadsWidget>(QString::fromStdString(m_config->value<std::string>("downloads.path").value())); - - m_urlRequestInterceptor = new UrlRequestInterceptor(QString::fromStdString(m_config->value<std::string>("filter.path").value())); - - // set default profile - m_defaultProfile = profile(QString::fromStdString(m_config->value<std::string>("browser.profile").value())); } -MainWindow *Browser::createWindow() +void Browser::setup(const QString &defaultProfile) { - // the window will delete itself when it closes, so we don't need to delete it - MainWindow *window = new MainWindow(m_config); - window->setBookmarksWidget(m_bookmarksManager); - window->setDownloadsWidget(m_downloadManager); - window->setProfile(m_defaultProfile); - window->addPlugins(m_plugins); + Q_ASSERT_X(m_config, "Browser::setup", "Configuration not set"); - m_windows.append(window); + // url request filter + m_urlFilter = std::make_shared<UrlRequestInterceptor>(QString::fromStdString(m_config->value<std::string>("filter.path").value())); - // has to be window.get(), but can't be *window - connect(window, &MainWindow::destroyed, this, [this, window]() { - m_windows.removeOne(window); - }); - window->show(); + // cookie request filter - return window; -} + // bookmarks + m_bookmarks = std::make_shared<BookmarksWidget>(QString::fromStdString(m_config->value<std::string>("bookmarks.path").value())); + connect(m_bookmarks.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { + m_windows.last()->createTab(url); + }); -MainWindow *Browser::createSession(const QString &profileName, bool newWindow, const QStringList &urls) -{ - MainWindow *window = nullptr; + // downloads + m_downloads = std::make_shared<DownloadsWidget>(QString::fromStdString(m_config->value<std::string>("downloads.path").value())); - // if we need to open in a new window, or there are no windows, make one - if(newWindow || m_windows.isEmpty()) { - window = createWindow(); - window->setProfile(profile(profileName)); + // load profiles + if(defaultProfile == "") { + auto *p = new WebProfile(this); + p->setRequestInterceptor(m_urlFilter.get()); + connect(p, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); + WebProfile::setDefaultProfile(p); } else { - // reverse-iterate through windows to check for window with the same profile - for(auto it = m_windows.rbegin(); it != m_windows.rend(); ++it) { - if((*it)->profile()->storageName() == profileName) { - window = *it; - break; - } - } - // if none is found, create one - if(window == nullptr) { - window = createWindow(); - window->setProfile(profile(profileName)); - } + auto *p = new WebProfile(defaultProfile, this); + p->setRequestInterceptor(m_urlFilter.get()); + connect(p, &WebProfile::downloadRequested, m_downloads.get(), &DownloadsWidget::addDownload); + WebProfile::setDefaultProfile(p); } +} - Q_CHECK_PTR(window); +void Browser::createSession(const QString &profileName, bool newWindow, const QStringList &urls) +{ + if(m_windows.isEmpty()) { + createWindow(); + } - if(urls.isEmpty()) { - // no URLs were given - window->newTab(QUrl::fromUserInput(m_config->value<std::string>("profile.homepage").value().c_str())); + auto *mainwindow = m_windows.last(); + if(newWindow) { + QUrl firstUrl = WebProfile::defaultProfile()->homepage(); + if(!urls.isEmpty()) + firstUrl = QUrl::fromUserInput(urls.at(0)); + auto *w = mainwindow->createSubWindow(firstUrl); + for(int i = 1; i < urls.count() - 1; i++) { + w->addTab(QUrl::fromUserInput(urls.at(i))); + } } else { for(const QString &url : urls) { - window->newTab(QUrl::fromUserInput(url)); + mainwindow->createTab(QUrl::fromUserInput(url)); } } - - return window; } -std::shared_ptr<WebEngineProfile> Browser::profile(const QString &storageName) +MainWindow *Browser::createWindow() { - if(m_profiles.contains(storageName)) { - return m_profiles[storageName]; - } - - // profile with name storageName has not been loaded - Q_ASSERT(m_config); - - const QString &path = QString::fromStdString(m_config->value<std::string>("profile.path").value()); - std::shared_ptr<WebEngineProfile> _profile; + // the window will delete itself when it closes, so we don't need to delete it + MainWindow *window = new MainWindow(m_config); + window->createSubWindow(WebProfile::defaultProfile()->newtab()); - if(storageName.isEmpty()) { - // create off-the-record profile - _profile = std::make_shared<WebEngineProfile>(nullptr); - _profile->loadProfile(m_config->section("profile"), path + "/otr.ini"); - } else { - // regular profile - _profile = std::make_shared<WebEngineProfile>(storageName, nullptr); - _profile->loadProfile(m_config->section("profile"), path + "/" + storageName + "/profile.ini"); - _profile->setPersistentStoragePath(path + "/storage"); - _profile->setCachePath(path + "/cache"); - } + auto *bookmarksAction = new QAction(tr("Bookmarks"), window); + bookmarksAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("bookmarks.shortcut").value()))); + connect(bookmarksAction, &QAction::triggered, window, [this, window]() { + window->addDockWidget(Qt::RightDockWidgetArea, m_bookmarks.get()); + }); + window->addAction(MainWindow::ToolsMenu, bookmarksAction); - _profile->setRequestInterceptor(m_urlRequestInterceptor); + auto *downloadsAction = new QAction(tr("Downloads"), window); + downloadsAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value<std::string>("downloads.shortcut").value()))); + connect(downloadsAction, &QAction::triggered, window, [this, window]() { + window->addDockWidget(Qt::RightDockWidgetArea, m_downloads.get()); + }); + window->addAction(MainWindow::ToolsMenu, downloadsAction); - connect(_profile.get(), &WebEngineProfile::downloadRequested, m_downloadManager.get(), &DownloadsWidget::addDownload); + m_windows.append(window); + connect(window, &MainWindow::destroyed, this, [this, window]() { + m_windows.removeOne(window); + }); - m_profiles.insert(storageName, _profile); - return _profile; + return window; } diff --git a/src/browser.h b/src/browser.h index f96d8c5..b43d45a 100644 --- a/src/browser.h +++ b/src/browser.h @@ -6,8 +6,8 @@ * SPDX-License-Identifier: GPL-3.0 */ -#ifndef BROWSER_H -#define BROWSER_H +#ifndef SMOLBOTE_BROWSER_H +#define SMOLBOTE_BROWSER_H #include "singleapplication.h" #include <QJsonObject> @@ -15,55 +15,34 @@ #include <memory> class Configuration; -class MainWindow; class BookmarksWidget; class DownloadsWidget; class UrlRequestInterceptor; -class WebEngineProfile; +class MainWindow; class Browser : public SingleApplication { Q_OBJECT public: - struct Plugin { - QJsonObject meta; - QObject *pointer; - }; - explicit Browser(int &argc, char *argv[]); ~Browser() final; Q_DISABLE_COPY(Browser) void setConfiguration(std::shared_ptr<Configuration> &config); - - std::shared_ptr<WebEngineProfile> profile(const QString &storageName); - - const QList<QString> profiles() const - { - return m_profiles.keys(); - } - - const QVector<Plugin> plugins() const - { - return m_plugins; - } + void setup(const QString &defaultProfile); public slots: - MainWindow *createSession(const QString &profileName, bool newWindow, const QStringList &urls); - -private: + void createSession(const QString &profileName, bool newWindow, const QStringList &urls); MainWindow *createWindow(); +private: std::shared_ptr<Configuration> m_config; + std::shared_ptr<BookmarksWidget> m_bookmarks; + std::shared_ptr<DownloadsWidget> m_downloads; + std::shared_ptr<UrlRequestInterceptor> m_urlFilter; QVector<MainWindow *> m_windows; - QVector<Plugin> m_plugins; - QHash<QString, std::shared_ptr<WebEngineProfile>> m_profiles; - std::shared_ptr<WebEngineProfile> m_defaultProfile; - UrlRequestInterceptor *m_urlRequestInterceptor = nullptr; - std::shared_ptr<BookmarksWidget> m_bookmarksManager; - std::shared_ptr<DownloadsWidget> m_downloadManager; }; -#endif // BROWSER_H +#endif // SMOLBOTE_BROWSER_H diff --git a/src/commandline.h b/src/commandline.h index f27041e..4229ac4 100644 --- a/src/commandline.h +++ b/src/commandline.h @@ -17,6 +17,7 @@ public: CommandLine(); void parseCommandLine(const QCoreApplication &app); + void printHelp(int exitCode = 0); void printVersion(int exitCode = 0); void printBuild(int exitCode = 0); diff --git a/src/forms/aboutdialog.cpp b/src/forms/aboutdialog.cpp deleted file mode 100644 index 058c57d..0000000 --- a/src/forms/aboutdialog.cpp +++ /dev/null @@ -1,98 +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 "aboutdialog.h" -#include "browser.h" -#include "ui_aboutdialog.h" -#include "version.h" -#include <QtWebEngine/QtWebEngineVersion> -#include <boost/version.hpp> - -// compiler -// clang also defines __GNUC__, so we need to check for clang first -#if defined(__clang__) -#define compiler "Clang " __clang_version__ -#elif defined(__GNUC__) -#define compiler "GCC " __VERSION__ -#else -#define compiler "unknown compiler"; -#endif - -AboutDialog::AboutDialog(QWidget *parent) - : QDialog(parent) - , ui(new Ui::AboutDialog) -{ - setAttribute(Qt::WA_DeleteOnClose, true); - ui->setupUi(this); - - auto *browser = dynamic_cast<Browser *>(QApplication::instance()); - - ui->icon->setPixmap(dynamic_cast<QApplication *>(browser)->windowIcon().pixmap(72, 72)); - - auto *aboutLabel = new QLabel(this); - aboutLabel->setWordWrap(true); - aboutLabel->setText(tr("<h2>smolbote " SMOLBOTE_VERSION "</h2>" - "<p><i>yet another no-frills browser</i></p>")); - ui->toolBox->addItem(aboutLabel, tr("About")); - - auto *licenseLabel = new QLabel(this); - licenseLabel->setWordWrap(true); - licenseLabel->setText(tr("<p>Copyright (C) 2017 Xian Nox</p>" - "<p>This program is free software, and you are welcome to use it under the conditions set by the GNU GPLv3:" - "<ul>" - "<li> the freedom to use the software for any purpose,</li>" - "<li> the freedom to change the software to suit your needs,</li>" - "<li> the freedom to share the software with anyone,</li>" - "<li> the freedom to share the changes you make, and</li>" - "<li> the responsibility to grant the same freedoms when sharing the software.</li>" - "</ul>" - "<p>You can find the full license text in LICENSE.md.</p>")); - ui->toolBox->addItem(licenseLabel, tr("License")); - - auto *libsLabel = new QLabel(this); - libsLabel->setWordWrap(true); - libsLabel->setText(tr("<h3>Version " SMOLBOTE_DESCRIBE "</h3>" - "<p>Branch: " SMOLBOTE_BRANCH "<br>" - "Compiled with " compiler "</p>" - "<p><ul>" - "<li>Qt " QT_VERSION_STR "</li>" - "<li>QtWebEngine " QTWEBENGINE_VERSION_STR "</li>" - "<li>Boost " BOOST_LIB_VERSION " </li>" - "</ul></p>")); - ui->toolBox->addItem(libsLabel, tr("Details")); - - // list profiles - if(!browser->profiles().isEmpty()) { - QString profilesText = tr("<p>Profile list:</p>" - "<p><ul>"); - for(const QString &name : browser->profiles()) { - if(name.isEmpty()) - profilesText += tr("<li>Off-the-record</li>"); - else - profilesText += tr("<li>%1</li>").arg(name); - } - profilesText += tr("</ul></p>"); - libsLabel->setText(libsLabel->text() + profilesText); - } - - // list plugins - if(!browser->plugins().isEmpty()) { - QString pluginText = tr("<p>Plugin list:</p>" - "<p><ul>"); - for(const Browser::Plugin &plugin : browser->plugins()) { - pluginText += tr("<li>%1 (%2)</li>").arg(plugin.meta["name"].toString(), plugin.meta["author"].toString()); - } - pluginText += tr("</ul></p>"); - libsLabel->setText(libsLabel->text() + pluginText); - } -} - -AboutDialog::~AboutDialog() -{ - delete ui; -} diff --git a/src/forms/aboutdialog.h b/src/forms/aboutdialog.h deleted file mode 100644 index 265f3c9..0000000 --- a/src/forms/aboutdialog.h +++ /dev/null @@ -1,31 +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_ABOUTDIALOG_H -#define SMOLBOTE_ABOUTDIALOG_H - -#include <QDialog> - -namespace Ui -{ -class AboutDialog; -} - -class AboutDialog : public QDialog -{ - Q_OBJECT - -public: - explicit AboutDialog(QWidget *parent = nullptr); - ~AboutDialog() override; - -private: - Ui::AboutDialog *ui; -}; - -#endif // SMOLBOTE_ABOUTDIALOG_H diff --git a/src/forms/aboutdialog.ui b/src/forms/aboutdialog.ui deleted file mode 100644 index f7fe21e..0000000 --- a/src/forms/aboutdialog.ui +++ /dev/null @@ -1,105 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ui version="4.0"> - <class>AboutDialog</class> - <widget class="QDialog" name="AboutDialog"> - <property name="geometry"> - <rect> - <x>0</x> - <y>0</y> - <width>500</width> - <height>600</height> - </rect> - </property> - <property name="windowTitle"> - <string>About</string> - </property> - <layout class="QHBoxLayout" name="horizontalLayout"> - <item> - <layout class="QVBoxLayout" name="verticalLayout_2"> - <item> - <widget class="QLabel" name="icon"> - <property name="minimumSize"> - <size> - <width>72</width> - <height>72</height> - </size> - </property> - <property name="text"> - <string>TextLabel</string> - </property> - </widget> - </item> - <item> - <spacer name="verticalSpacer"> - <property name="orientation"> - <enum>Qt::Vertical</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>20</width> - <height>40</height> - </size> - </property> - </spacer> - </item> - </layout> - </item> - <item> - <layout class="QVBoxLayout" name="verticalLayout"> - <item> - <widget class="QToolBox" name="toolBox"> - <property name="currentIndex"> - <number>-1</number> - </property> - </widget> - </item> - <item> - <widget class="QDialogButtonBox" name="buttonBox"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="standardButtons"> - <set>QDialogButtonBox::Close</set> - </property> - </widget> - </item> - </layout> - </item> - </layout> - </widget> - <resources/> - <connections> - <connection> - <sender>buttonBox</sender> - <signal>accepted()</signal> - <receiver>AboutDialog</receiver> - <slot>accept()</slot> - <hints> - <hint type="sourcelabel"> - <x>248</x> - <y>254</y> - </hint> - <hint type="destinationlabel"> - <x>157</x> - <y>274</y> - </hint> - </hints> - </connection> - <connection> - <sender>buttonBox</sender> - <signal>rejected()</signal> - <receiver>AboutDialog</receiver> - <slot>reject()</slot> - <hints> - <hint type="sourcelabel"> - <x>316</x> - <y>260</y> - </hint> - <hint type="destinationlabel"> - <x>286</x> - <y>274</y> - </hint> - </hints> - </connection> - </connections> -</ui> diff --git a/src/main.cpp b/src/main.cpp index 75691f9..9fd009b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,47 +6,24 @@ * SPDX-License-Identifier: GPL-3.0 */ -#include "browser.h" #include "commandline.h" #include "mainwindow/mainwindow.h" #include "version.h" -#include <iomanip> -#include <iostream> -#include "configuration/configuration.h" -// startup time measuring -#ifdef QT_DEBUG -#include <QElapsedTimer> -#endif +#include "webengine/webprofile.h" +#include "browser.h" +#include <configuration/configuration.h> +#include <memory> int main(int argc, char **argv) { - // Create application object - Browser instance(argc, argv); - instance.setApplicationVersion(SMOLBOTE_VERSION); - -#ifdef QT_DEBUG - QElapsedTimer timer; - timer.start(); -#endif + Browser app(argc, argv); CommandLine parser; - parser.parseCommandLine(instance); - -#ifdef QT_DEBUG - qDebug("config=%s", qUtf8Printable(parser.value(parser.configOption))); - qDebug("profile=%s", qUtf8Printable(parser.value(parser.profileOption))); - qDebug("socket=%s", qUtf8Printable(parser.value(parser.socketOption))); - -#endif + parser.parseCommandLine(app); + // create and load configuration std::shared_ptr<Configuration> config = std::make_shared<Configuration>(); - - // load user configuration - if(!parser.value(parser.configOption).isEmpty()) { - qDebug("Reading configuration [%s]: %s", - qUtf8Printable(parser.value(parser.configOption)), - config->read(parser.value(parser.configOption)) ? "ok" : "failed"); - } + config->read(parser.value(parser.configOption)); // parse command-line overrides // we assume the users knows what they're doing, so we only pass the unknown options to program_options @@ -59,43 +36,37 @@ int main(int argc, char **argv) const char *_argv[_argc]; // program_options requires 0 to be the program name, otherwise it seems to fail - _argv[0] = qUtf8Printable(instance.arguments().at(0)); + _argv[0] = qUtf8Printable(app.arguments().at(0)); for(int i = 1; i < _argc; ++i) { _argv[i] = qUtf8Printable(QString("--%1=%2").arg(parser.unknownOptions[i - 1].names().at(0), parser.value(parser.unknownOptions[i - 1]))); } - qDebug("Parsing command-line overrides: %s", config->parse(_argc, _argv) ? "ok" : "failed"); + config->parse(_argc, _argv); } - // check for other instances - bool isSingleInstance = instance.bindLocalSocket(parser.value(parser.socketOption)); - if(isSingleInstance) { - qDebug("Bound to local socket %s", qUtf8Printable(instance.serverName())); - instance.setConfiguration(config); - } + app.setConfiguration(config); + + // set up socket + bool isSingleInstance = app.bindLocalSocket(parser.value(parser.socketOption)); +#ifdef QT_DEBUG + qDebug("bindLocalSocket(%s) = %s", qUtf8Printable(parser.value(parser.socketOption)), isSingleInstance ? "true" : "false"); +#endif - // create session - { - QString profile; + // if we are the only instance, set up the browser + if(isSingleInstance) { if(parser.isSet(parser.profileOption)) - profile = parser.value(parser.profileOption); + app.setup(parser.value(parser.profileOption)); else - profile = QString::fromStdString(config->value<std::string>("browser.profile").value()); + app.setup(QString::fromStdString(config->value<std::string>("profile.default").value())); - instance.sendMessage(profile, parser.isSet(parser.newWindowOption), parser.positionalArguments()); + QObject::connect(&app, &Browser::messageAvailable, &app, &Browser::createSession); } - if(!isSingleInstance) { - return 0; - } - -#ifdef QT_DEBUG - qDebug("Startup complete in %lldms", timer.elapsed()); -#endif + app.sendMessage(parser.value(parser.profileOption), parser.isSet(parser.newWindowOption), parser.positionalArguments()); - // Normally we'd use - //return instance.exec(); - // but, Call to "exec" is ambiguous - return static_cast<QApplication *>(&instance)->exec(); + if(isSingleInstance) + return app.exec(); + else + return 0; } 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 diff --git a/src/version.h.in b/src/version.h.in index a5c3204..03d00d1 100644 --- a/src/version.h.in +++ b/src/version.h.in @@ -1,12 +1,19 @@ -#ifndef VERSION_H -#define VERSION_H +#ifndef SMOLBOTE_VERSION_H +#define SMOLBOTE_VERSION_H // VcsVersion gets defined by the CMakeLists if project-src/.hg exists #cmakedefine VcsVersion +#cmakedefine VcsBookmark #ifdef VcsVersion + +#ifdef VcsBookmark +// Version: 1.2.3 (stable) +#define SMOLBOTE_VERSION "@VcsVersion@ (@VcsBookmark@)" +#else // Version: 1.2.3 #define SMOLBOTE_VERSION "@VcsVersion@" +#endif // Describe: 1.2.3-r123-0123456789ab #define SMOLBOTE_DESCRIBE "@VcsVersion@-r@VcsRevision@-@VcsCommit@" @@ -18,6 +25,8 @@ #define SMOLBOTE_BRANCH "@VcsBranch@" #else + +// If VcsVersion has not been defined, these values will be used // Version: 1.2.3 #define SMOLBOTE_VERSION "0.3.0" @@ -32,4 +41,4 @@ #endif -#endif //VERSION_H +#endif // SMOLBOTE_VERSION_H diff --git a/src/webengine/urlinterceptor.h b/src/webengine/urlinterceptor.h index af15f99..951eb52 100644 --- a/src/webengine/urlinterceptor.h +++ b/src/webengine/urlinterceptor.h @@ -9,8 +9,8 @@ #ifndef URLREQUESTINTERCEPTOR_H #define URLREQUESTINTERCEPTOR_H -#include <QWebEngineUrlRequestInterceptor> #include <QMutex> +#include <QWebEngineUrlRequestInterceptor> class UrlRequestInterceptor : public QWebEngineUrlRequestInterceptor { diff --git a/src/webengine/webengineprofile.cpp b/src/webengine/webengineprofile.cpp index 55af9f2..e69de29 100644 --- a/src/webengine/webengineprofile.cpp +++ b/src/webengine/webengineprofile.cpp @@ -1,214 +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 "webengineprofile.h" -#include <QFileInfo> -#include <QSettings> -#include <QWebEngineCookieStore> -#include <QWebEngineSettings> - -WebEngineProfile::WebEngineProfile(QObject *parent) - : QWebEngineProfile(parent) -{ - m_name = tr("Off-the-record"); - -#ifdef QT_DEBUG - qDebug("Creating off-the-record profile"); -#endif - - // Off-the-record profiles have no persistent path -} - -WebEngineProfile::WebEngineProfile(const QString &name, QObject *parent) - : QWebEngineProfile(name, parent) -{ - m_name = name; - -#ifdef QT_DEBUG - qDebug("Creating profile %s", qUtf8Printable(m_name)); -#endif -} - -WebEngineProfile::~WebEngineProfile() = default; - -QString WebEngineProfile::name() const -{ - return m_name; -} - -QUrl WebEngineProfile::homepage() const -{ - return m_homepage; -} - -QUrl WebEngineProfile::newtab() const -{ - return m_newtab; -} - -void WebEngineProfile::loadProfile(QHash<QString, QString> conf, const QString &path) -{ - m_configPath = path; - -#ifdef QT_DEBUG - qDebug("Reading config for profile '%s': %s", qUtf8Printable(m_name), qUtf8Printable(m_configPath)); -#endif - QSettings config(m_configPath, QSettings::IniFormat); - - m_homepage = config.value("homepage", conf["profile.homepage"]).toUrl(); - m_newtab = config.value("newtab", conf["profile.newtab"]).toUrl(); - - config.beginGroup("http"); - setHttpUserAgent(config.value("userAgent", httpUserAgent()).toString()); - setHttpAcceptLanguage(config.value("accept-lang", httpAcceptLanguage()).toString()); - { - QString cacheType = config.value("cacheType").toString(); - if(cacheType == "memory") { - setHttpCacheType(QWebEngineProfile::MemoryHttpCache); - } else if(cacheType == "disk") { - setHttpCacheType(QWebEngineProfile::DiskHttpCache); - } else if(cacheType == "disabled") { - setHttpCacheType(QWebEngineProfile::NoCache); - } - } - setHttpCacheMaximumSize(config.value("cacheSize", httpCacheMaximumSize()).toInt()); - config.endGroup(); // http - - config.beginGroup("policy"); - { - QString cookies = config.value("cookies").toString(); - if(cookies == "disabled") { - setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); - } else if(cookies == "allow") { - setPersistentCookiesPolicy(QWebEngineProfile::AllowPersistentCookies); - } else if(cookies == "force") { - setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); - } - } - config.endGroup(); // policy - - config.beginGroup("attributes"); - QWebEngineSettings *s = settings(); - s->setAttribute(QWebEngineSettings::AutoLoadImages, - config.value("autoLoadImages", s->testAttribute(QWebEngineSettings::AutoLoadImages)).toBool()); - s->setAttribute(QWebEngineSettings::JavascriptEnabled, - config.value("javascriptEnabled", s->testAttribute(QWebEngineSettings::JavascriptEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, - config.value("javascriptCanOpenWindows", s->testAttribute(QWebEngineSettings::JavascriptCanOpenWindows)).toBool()); - s->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, - config.value("javascriptCanAccessClipboard", s->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard)).toBool()); - s->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, - config.value("linksIncludedInFocusChain", s->testAttribute(QWebEngineSettings::LinksIncludedInFocusChain)).toBool()); - s->setAttribute(QWebEngineSettings::LocalStorageEnabled, - config.value("localStorageEnabled", s->testAttribute(QWebEngineSettings::LocalStorageEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, - config.value("localContentCanAccessRemoteUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls)).toBool()); - s->setAttribute(QWebEngineSettings::XSSAuditingEnabled, - config.value("xssAuditingEnabled", s->testAttribute(QWebEngineSettings::XSSAuditingEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, - config.value("spatialNavigationEnabled", s->testAttribute(QWebEngineSettings::SpatialNavigationEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, - config.value("localContentCanAccessFileUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)).toBool()); - s->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, - config.value("hyperlinkAuditingEnabled", s->testAttribute(QWebEngineSettings::HyperlinkAuditingEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, - config.value("scrollAnimatorEnabled", s->testAttribute(QWebEngineSettings::ScrollAnimatorEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::ErrorPageEnabled, - config.value("errorPageEnabled", s->testAttribute(QWebEngineSettings::ErrorPageEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::PluginsEnabled, - config.value("pluginsEnabled", s->testAttribute(QWebEngineSettings::PluginsEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, - config.value("fullscreenSupportEnabled", s->testAttribute(QWebEngineSettings::FullScreenSupportEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, - config.value("screenCaptureEnabled", s->testAttribute(QWebEngineSettings::ScreenCaptureEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::WebGLEnabled, - config.value("webglEnabled", s->testAttribute(QWebEngineSettings::WebGLEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, - config.value("accelerated2dCanvasEnabled", s->testAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, - config.value("autoLoadIconsForPage", s->testAttribute(QWebEngineSettings::AutoLoadIconsForPage)).toBool()); - s->setAttribute(QWebEngineSettings::TouchIconsEnabled, - config.value("touchIconsEnabled", s->testAttribute(QWebEngineSettings::TouchIconsEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, - config.value("focusOnNavigationEnabled", s->testAttribute(QWebEngineSettings::FocusOnNavigationEnabled)).toBool()); - s->setAttribute(QWebEngineSettings::PrintElementBackgrounds, - config.value("printElementBackgrounds", s->testAttribute(QWebEngineSettings::PrintElementBackgrounds)).toBool()); - s->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, - config.value("allowRunningInsecureContent", s->testAttribute(QWebEngineSettings::AllowRunningInsecureContent)).toBool()); - config.endGroup(); // attributes -} - -void WebEngineProfile::saveProfile(const QString &path) -{ - QSettings config(persistentStoragePath() + "/profile.ini", QSettings::IniFormat); - - config.setValue("homepage", homepage().toString()); - config.setValue("newtab", newtab().toString()); - - config.beginGroup("http"); - config.setValue("userAgent", httpUserAgent()); - config.setValue("accept-lang", httpAcceptLanguage()); - switch(httpCacheType()) { - case MemoryHttpCache: - config.setValue("cacheType", "memory"); - break; - case DiskHttpCache: - config.setValue("cacheType", "disk"); - break; - case NoCache: - config.setValue("cacheType", "disabled"); - break; - } - config.setValue("cacheSize", httpCacheMaximumSize()); - config.endGroup(); // http - - config.beginGroup("policy"); - switch(persistentCookiesPolicy()) { - case NoPersistentCookies: - config.setValue("cookies", "disabled"); - break; - case AllowPersistentCookies: - config.setValue("cookies", "allow"); - break; - case ForcePersistentCookies: - config.setValue("cookies", "force"); - break; - } - config.endGroup(); // policy - - QWebEngineSettings *s = settings(); - config.beginGroup("attributes"); - config.setValue("autoLoadImages", s->testAttribute(QWebEngineSettings::AutoLoadImages)); - config.setValue("javascriptEnabled", s->testAttribute(QWebEngineSettings::JavascriptEnabled)); - config.setValue("javascriptCanOpenWindows", s->testAttribute(QWebEngineSettings::JavascriptCanOpenWindows)); - config.setValue("javascriptCanAccessClipboard", s->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard)); - config.setValue("linksIncludedInFocusChain", s->testAttribute(QWebEngineSettings::LinksIncludedInFocusChain)); - config.setValue("localStorageEnabled", s->testAttribute(QWebEngineSettings::LocalStorageEnabled)); - config.setValue("localContentCanAccessRemoteUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls)); - config.setValue("xssAuditingEnabled", s->testAttribute(QWebEngineSettings::XSSAuditingEnabled)); - config.setValue("spatialNavigationEnabled", s->testAttribute(QWebEngineSettings::SpatialNavigationEnabled)); - config.setValue("localContentCanAccessFileUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)); - config.setValue("hyperlinkAuditingEnabled", s->testAttribute(QWebEngineSettings::HyperlinkAuditingEnabled)); - config.setValue("scrollAnimatorEnabled", s->testAttribute(QWebEngineSettings::ScrollAnimatorEnabled)); - config.setValue("errorPageEnabled", s->testAttribute(QWebEngineSettings::ErrorPageEnabled)); - config.setValue("pluginsEnabled", s->testAttribute(QWebEngineSettings::PluginsEnabled)); - config.setValue("fullscreenSupportEnabled", s->testAttribute(QWebEngineSettings::FullScreenSupportEnabled)); - config.setValue("screenCaptureEnabled", s->testAttribute(QWebEngineSettings::ScreenCaptureEnabled)); - config.setValue("webglEnabled", s->testAttribute(QWebEngineSettings::WebGLEnabled)); - config.setValue("accelerated2dCanvasEnabled", s->testAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled)); - config.setValue("autoLoadIconsForPage", s->testAttribute(QWebEngineSettings::AutoLoadIconsForPage)); - config.setValue("touchIconsEnabled", s->testAttribute(QWebEngineSettings::TouchIconsEnabled)); -#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) - config.setValue("focusOnNavigationEnabled", s->testAttribute(QWebEngineSettings::FocusOnNavigationEnabled)); - config.setValue("printElementBackgrounds", s->testAttribute(QWebEngineSettings::PrintElementBackgrounds)); - config.setValue("allowRunningInsecureContent", s->testAttribute(QWebEngineSettings::AllowRunningInsecureContent)); -#endif - config.endGroup(); // attributes - - config.sync(); -} diff --git a/src/webengine/webengineprofile.h b/src/webengine/webengineprofile.h index a38c977..e69de29 100644 --- a/src/webengine/webengineprofile.h +++ b/src/webengine/webengineprofile.h @@ -1,39 +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_WEBENGINEPROFILE_H -#define SMOLBOTE_WEBENGINEPROFILE_H - -#include <QUrl> -#include <QWebEngineProfile> - -class WebEngineProfile : public QWebEngineProfile -{ - Q_OBJECT -public: - explicit WebEngineProfile(QObject *parent = nullptr); - explicit WebEngineProfile(const QString &name, QObject *parent = nullptr); - - ~WebEngineProfile() override; - - QString name() const; - QUrl homepage() const; - QUrl newtab() const; - -public slots: - void loadProfile(QHash<QString, QString> conf, const QString &path); - void saveProfile(const QString &path = QString()); - -private: - QString m_configPath; - QString m_name; - QUrl m_homepage = QUrl("about:blank"); - QUrl m_newtab = QUrl("about:blank"); -}; - -#endif // SMOLBOTE_WEBENGINEPROFILE_H diff --git a/src/webengine/webprofile.cpp b/src/webengine/webprofile.cpp new file mode 100644 index 0000000..24b23a0 --- /dev/null +++ b/src/webengine/webprofile.cpp @@ -0,0 +1,216 @@ +/* + * 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 "webprofile.h" +#include <QFileInfo> +#include <QSettings> +#include <QWebEngineCookieStore> +#include <QWebEngineSettings> + +WebProfile *WebProfile::profile = nullptr; + +WebProfile::WebProfile(QObject *parent) + : QWebEngineProfile(parent) +{ + m_name = tr("Off-the-record"); + +#ifdef QT_DEBUG + qDebug("Creating off-the-record profile"); +#endif + + // Off-the-record profiles have no persistent path +} + +WebProfile::WebProfile(const QString &name, QObject *parent) + : QWebEngineProfile(name, parent) +{ + m_name = name; + +#ifdef QT_DEBUG + qDebug("Creating profile %s", qUtf8Printable(m_name)); +#endif +} + +WebProfile::~WebProfile() = default; + +QString WebProfile::name() const +{ + return m_name; +} + +QUrl WebProfile::homepage() const +{ + return m_homepage; +} + +QUrl WebProfile::newtab() const +{ + return m_newtab; +} + +void WebProfile::loadProfile(QHash<QString, QString> conf, const QString &path) +{ + m_configPath = path; + +#ifdef QT_DEBUG + qDebug("Reading config for profile '%s': %s", qUtf8Printable(m_name), qUtf8Printable(m_configPath)); +#endif + QSettings config(m_configPath, QSettings::IniFormat); + + m_homepage = config.value("homepage", conf["profile.homepage"]).toUrl(); + m_newtab = config.value("newtab", conf["profile.newtab"]).toUrl(); + + config.beginGroup("http"); + setHttpUserAgent(config.value("userAgent", httpUserAgent()).toString()); + setHttpAcceptLanguage(config.value("accept-lang", httpAcceptLanguage()).toString()); + { + QString cacheType = config.value("cacheType").toString(); + if(cacheType == "memory") { + setHttpCacheType(QWebEngineProfile::MemoryHttpCache); + } else if(cacheType == "disk") { + setHttpCacheType(QWebEngineProfile::DiskHttpCache); + } else if(cacheType == "disabled") { + setHttpCacheType(QWebEngineProfile::NoCache); + } + } + setHttpCacheMaximumSize(config.value("cacheSize", httpCacheMaximumSize()).toInt()); + config.endGroup(); // http + + config.beginGroup("policy"); + { + QString cookies = config.value("cookies").toString(); + if(cookies == "disabled") { + setPersistentCookiesPolicy(QWebEngineProfile::NoPersistentCookies); + } else if(cookies == "allow") { + setPersistentCookiesPolicy(QWebEngineProfile::AllowPersistentCookies); + } else if(cookies == "force") { + setPersistentCookiesPolicy(QWebEngineProfile::ForcePersistentCookies); + } + } + config.endGroup(); // policy + + config.beginGroup("attributes"); + QWebEngineSettings *s = settings(); + s->setAttribute(QWebEngineSettings::AutoLoadImages, + config.value("autoLoadImages", s->testAttribute(QWebEngineSettings::AutoLoadImages)).toBool()); + s->setAttribute(QWebEngineSettings::JavascriptEnabled, + config.value("javascriptEnabled", s->testAttribute(QWebEngineSettings::JavascriptEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::JavascriptCanOpenWindows, + config.value("javascriptCanOpenWindows", s->testAttribute(QWebEngineSettings::JavascriptCanOpenWindows)).toBool()); + s->setAttribute(QWebEngineSettings::JavascriptCanAccessClipboard, + config.value("javascriptCanAccessClipboard", s->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard)).toBool()); + s->setAttribute(QWebEngineSettings::LinksIncludedInFocusChain, + config.value("linksIncludedInFocusChain", s->testAttribute(QWebEngineSettings::LinksIncludedInFocusChain)).toBool()); + s->setAttribute(QWebEngineSettings::LocalStorageEnabled, + config.value("localStorageEnabled", s->testAttribute(QWebEngineSettings::LocalStorageEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls, + config.value("localContentCanAccessRemoteUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls)).toBool()); + s->setAttribute(QWebEngineSettings::XSSAuditingEnabled, + config.value("xssAuditingEnabled", s->testAttribute(QWebEngineSettings::XSSAuditingEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::SpatialNavigationEnabled, + config.value("spatialNavigationEnabled", s->testAttribute(QWebEngineSettings::SpatialNavigationEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls, + config.value("localContentCanAccessFileUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)).toBool()); + s->setAttribute(QWebEngineSettings::HyperlinkAuditingEnabled, + config.value("hyperlinkAuditingEnabled", s->testAttribute(QWebEngineSettings::HyperlinkAuditingEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::ScrollAnimatorEnabled, + config.value("scrollAnimatorEnabled", s->testAttribute(QWebEngineSettings::ScrollAnimatorEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::ErrorPageEnabled, + config.value("errorPageEnabled", s->testAttribute(QWebEngineSettings::ErrorPageEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::PluginsEnabled, + config.value("pluginsEnabled", s->testAttribute(QWebEngineSettings::PluginsEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::FullScreenSupportEnabled, + config.value("fullscreenSupportEnabled", s->testAttribute(QWebEngineSettings::FullScreenSupportEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, + config.value("screenCaptureEnabled", s->testAttribute(QWebEngineSettings::ScreenCaptureEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::WebGLEnabled, + config.value("webglEnabled", s->testAttribute(QWebEngineSettings::WebGLEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled, + config.value("accelerated2dCanvasEnabled", s->testAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::AutoLoadIconsForPage, + config.value("autoLoadIconsForPage", s->testAttribute(QWebEngineSettings::AutoLoadIconsForPage)).toBool()); + s->setAttribute(QWebEngineSettings::TouchIconsEnabled, + config.value("touchIconsEnabled", s->testAttribute(QWebEngineSettings::TouchIconsEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::FocusOnNavigationEnabled, + config.value("focusOnNavigationEnabled", s->testAttribute(QWebEngineSettings::FocusOnNavigationEnabled)).toBool()); + s->setAttribute(QWebEngineSettings::PrintElementBackgrounds, + config.value("printElementBackgrounds", s->testAttribute(QWebEngineSettings::PrintElementBackgrounds)).toBool()); + s->setAttribute(QWebEngineSettings::AllowRunningInsecureContent, + config.value("allowRunningInsecureContent", s->testAttribute(QWebEngineSettings::AllowRunningInsecureContent)).toBool()); + config.endGroup(); // attributes +} + +void WebProfile::saveProfile(const QString &path) +{ + QSettings config(persistentStoragePath() + "/profile.ini", QSettings::IniFormat); + + config.setValue("homepage", homepage().toString()); + config.setValue("newtab", newtab().toString()); + + config.beginGroup("http"); + config.setValue("userAgent", httpUserAgent()); + config.setValue("accept-lang", httpAcceptLanguage()); + switch(httpCacheType()) { + case MemoryHttpCache: + config.setValue("cacheType", "memory"); + break; + case DiskHttpCache: + config.setValue("cacheType", "disk"); + break; + case NoCache: + config.setValue("cacheType", "disabled"); + break; + } + config.setValue("cacheSize", httpCacheMaximumSize()); + config.endGroup(); // http + + config.beginGroup("policy"); + switch(persistentCookiesPolicy()) { + case NoPersistentCookies: + config.setValue("cookies", "disabled"); + break; + case AllowPersistentCookies: + config.setValue("cookies", "allow"); + break; + case ForcePersistentCookies: + config.setValue("cookies", "force"); + break; + } + config.endGroup(); // policy + + QWebEngineSettings *s = settings(); + config.beginGroup("attributes"); + config.setValue("autoLoadImages", s->testAttribute(QWebEngineSettings::AutoLoadImages)); + config.setValue("javascriptEnabled", s->testAttribute(QWebEngineSettings::JavascriptEnabled)); + config.setValue("javascriptCanOpenWindows", s->testAttribute(QWebEngineSettings::JavascriptCanOpenWindows)); + config.setValue("javascriptCanAccessClipboard", s->testAttribute(QWebEngineSettings::JavascriptCanAccessClipboard)); + config.setValue("linksIncludedInFocusChain", s->testAttribute(QWebEngineSettings::LinksIncludedInFocusChain)); + config.setValue("localStorageEnabled", s->testAttribute(QWebEngineSettings::LocalStorageEnabled)); + config.setValue("localContentCanAccessRemoteUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessRemoteUrls)); + config.setValue("xssAuditingEnabled", s->testAttribute(QWebEngineSettings::XSSAuditingEnabled)); + config.setValue("spatialNavigationEnabled", s->testAttribute(QWebEngineSettings::SpatialNavigationEnabled)); + config.setValue("localContentCanAccessFileUrls", s->testAttribute(QWebEngineSettings::LocalContentCanAccessFileUrls)); + config.setValue("hyperlinkAuditingEnabled", s->testAttribute(QWebEngineSettings::HyperlinkAuditingEnabled)); + config.setValue("scrollAnimatorEnabled", s->testAttribute(QWebEngineSettings::ScrollAnimatorEnabled)); + config.setValue("errorPageEnabled", s->testAttribute(QWebEngineSettings::ErrorPageEnabled)); + config.setValue("pluginsEnabled", s->testAttribute(QWebEngineSettings::PluginsEnabled)); + config.setValue("fullscreenSupportEnabled", s->testAttribute(QWebEngineSettings::FullScreenSupportEnabled)); + config.setValue("screenCaptureEnabled", s->testAttribute(QWebEngineSettings::ScreenCaptureEnabled)); + config.setValue("webglEnabled", s->testAttribute(QWebEngineSettings::WebGLEnabled)); + config.setValue("accelerated2dCanvasEnabled", s->testAttribute(QWebEngineSettings::Accelerated2dCanvasEnabled)); + config.setValue("autoLoadIconsForPage", s->testAttribute(QWebEngineSettings::AutoLoadIconsForPage)); + config.setValue("touchIconsEnabled", s->testAttribute(QWebEngineSettings::TouchIconsEnabled)); +#if QT_VERSION >= QT_VERSION_CHECK(5, 8, 0) + config.setValue("focusOnNavigationEnabled", s->testAttribute(QWebEngineSettings::FocusOnNavigationEnabled)); + config.setValue("printElementBackgrounds", s->testAttribute(QWebEngineSettings::PrintElementBackgrounds)); + config.setValue("allowRunningInsecureContent", s->testAttribute(QWebEngineSettings::AllowRunningInsecureContent)); +#endif + config.endGroup(); // attributes + + config.sync(); +} diff --git a/src/webengine/webprofile.h b/src/webengine/webprofile.h new file mode 100644 index 0000000..41d1aec --- /dev/null +++ b/src/webengine/webprofile.h @@ -0,0 +1,53 @@ +/* + * 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_WEBENGINEPROFILE_H +#define SMOLBOTE_WEBENGINEPROFILE_H + +#include <QHash> +#include <QUrl> +#include <QWebEngineProfile> + +class WebProfile : public QWebEngineProfile +{ + Q_OBJECT +public: + explicit WebProfile(QObject *parent = nullptr); + explicit WebProfile(const QString &name, QObject *parent = nullptr); + + ~WebProfile() override; + + static void setDefaultProfile(WebProfile *profile) + { + Q_CHECK_PTR(profile); + WebProfile::profile = profile; + } + static WebProfile *defaultProfile() + { + Q_CHECK_PTR(WebProfile::profile); + return WebProfile::profile; + } + + QString name() const; + QUrl homepage() const; + QUrl newtab() const; + +public slots: + void loadProfile(QHash<QString, QString> conf, const QString &path); + void saveProfile(const QString &path = QString()); + +private: + static WebProfile *profile; + + QString m_configPath; + QString m_name; + QUrl m_homepage = QUrl("about:blank"); + QUrl m_newtab = QUrl("about:blank"); +}; + +#endif // SMOLBOTE_WEBENGINEPROFILE_H diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp index 7734aa0..d145d2b 100644 --- a/src/webengine/webview.cpp +++ b/src/webengine/webview.cpp @@ -7,19 +7,23 @@ */ #include "webview.h" -#include "mainwindow/mainwindow.h" -#include "mainwindow/widgets/tabbar.h" +#include "webpage.h" +#include "webprofile.h" #include "widgets/pagemenu.h" #include "widgets/pagetoolsmenu.h" #include <QDialog> #include <QStatusBar> #include <QVBoxLayout> +#include "mainwindow/window.h" -WebView::WebView(MainWindow *parentMainWindow, QWidget *parent) +WebView::WebView(WebProfile *profile, QWidget *parent) : QWebEngineView(parent) { - Q_CHECK_PTR(parentMainWindow); - m_parent = parentMainWindow; + Q_CHECK_PTR(profile); + m_profile = profile; + setPage(new WebPage(profile, this)); + + m_parentWindow = qobject_cast<Window *>(parent); // load status and progress connect(this, &QWebEngineView::loadStarted, this, [this]() { @@ -72,26 +76,32 @@ int WebView::loadProgress() const WebView *WebView::createWindow(QWebEnginePage::WebWindowType type) { - WebView *view = new WebView(m_parent); + if(m_parentWindow == nullptr) { + qDebug("parent window not found!"); + return nullptr; + } + + // parent Window has been found + WebView *view = new WebView(m_profile, m_parentWindow); switch(type) { case QWebEnginePage::WebBrowserWindow: // a complete web browser window - m_parent->newWindow()->tabBar->addTab(view); + m_parentWindow->addTab(view); break; case QWebEnginePage::WebBrowserTab: // a web browser tab - m_parent->tabBar->setCurrentIndex(m_parent->tabBar->addTab(view)); + m_parentWindow->swapToTab(m_parentWindow->addTab(view)); break; case QWebEnginePage::WebDialog: // a window without decorations - m_parent->newWindow()->tabBar->addTab(view); + m_parentWindow->addTab(view); break; case QWebEnginePage::WebBrowserBackgroundTab: // a web browser tab, but don't swap to it - m_parent->tabBar->addTab(view); + m_parentWindow->addTab(view); break; } @@ -100,26 +110,17 @@ WebView *WebView::createWindow(QWebEnginePage::WebWindowType type) void WebView::handleLinkHovered(const QString &url) { - if(isVisible()) { - m_parent->statusBar()->showMessage(url, 3000); - } + // TODO: tooltip + qDebug("%s", qUtf8Printable(url)); } void WebView::triggerViewAction(WebView::ViewAction action) { switch(action) { + case GoHome: + load(m_profile->homepage()); case BookmarkPage: emit newBookmark(this->title(), this->url()); break; } } - -WebView *createWebView(const QUrl &url, WebEngineProfile *profile, MainWindow *parent) -{ - auto *view = new WebView(parent); - auto *page = new WebPage(profile); - view->setPage(page); - page->load(url); - - return view; -} diff --git a/src/webengine/webview.h b/src/webengine/webview.h index 233565e..a27a61f 100644 --- a/src/webengine/webview.h +++ b/src/webengine/webview.h @@ -13,17 +13,18 @@ #include <QWebEngineView> class QMenu; -class MainWindow; -class WebEngineProfile; +class WebProfile; +class Window; class WebView : public QWebEngineView { Q_OBJECT public: enum ViewAction { + GoHome, BookmarkPage }; - explicit WebView(MainWindow *parentMainWindow, QWidget *parent = nullptr); + explicit WebView(WebProfile *profile = nullptr, QWidget *parent = nullptr); ~WebView() override; QMenu *pageMenu(); @@ -53,7 +54,9 @@ private slots: void handleLinkHovered(const QString &url); private: - MainWindow *m_parent = nullptr; + Window *m_parentWindow = nullptr; + WebProfile *m_profile = nullptr; + QMenu *m_pageMenu = nullptr; QMenu *m_toolsMenu = nullptr; @@ -61,6 +64,4 @@ private: int m_loadProgress; }; -WebView *createWebView(const QUrl &url, WebEngineProfile *profile, MainWindow *parent); - #endif // SMOLBOTE_WEBVIEW_H diff --git a/src/webengine/widgets/pagetoolsmenu.cpp b/src/webengine/widgets/pagetoolsmenu.cpp index 381c6f4..2a227ea 100644 --- a/src/webengine/widgets/pagetoolsmenu.cpp +++ b/src/webengine/widgets/pagetoolsmenu.cpp @@ -52,7 +52,7 @@ QDialog *PageToolsMenu::createDevToolsDialog(QWebEnginePage *page) auto *devPage = new QWebEnginePage(view); view->setPage(devPage); -// page->setDevToolsPage(devPage); + // page->setDevToolsPage(devPage); auto *l = new QVBoxLayout(popup); l->setContentsMargins(0, 0, 0, 0); diff --git a/src/widgets/mainwindowmenubar.cpp b/src/widgets/mainwindowmenubar.cpp deleted file mode 100644 index 44a6eba..0000000 --- a/src/widgets/mainwindowmenubar.cpp +++ /dev/null @@ -1,91 +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 "mainwindowmenubar.h" -#include "browser.h" -#include "downloads/downloadswidget.h" -#include "mainwindow/mainwindow.h" -#include <QInputDialog> -#include <QShortcut> -#include "configuration/configuration.h" - -MainWindowMenuBar::MainWindowMenuBar(std::shared_ptr<Configuration> config, MainWindow *parent) - : QMenuBar(parent) -{ - Q_ASSERT(config); - Q_CHECK_PTR(parent); - - setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); - - auto *browser = dynamic_cast<Browser *>(QApplication::instance()); - - // Browser menu - QMenu *browserMenu = new QMenu(qApp->applicationName(), this); - addMenu(browserMenu); - - QAction *newWindowAction = browserMenu->addAction(tr("New Window")); - connect(newWindowAction, &QAction::triggered, parent, [parent]() { - parent->newWindow(); - }); - newWindowAction->setShortcut(QKeySequence(config->value<std::string>("browser.shortcuts.newWindow").value().c_str())); - - browserMenu->addAction(parent->findChild<QAction*>("newTab_action")); - - browserMenu->addSeparator(); - //browserMenu->addAction(tr("Settings"), parent, &MainWindow::showSettingsDialog); - browserMenu->addAction(tr("About"), parent, &MainWindow::about, QKeySequence(config->value<std::string>("browser.shortcuts.about").value().c_str())); - browserMenu->addAction(tr("About Qt"), qApp, &QApplication::aboutQt); - browserMenu->addSeparator(); - - QAction *quitAction = browserMenu->addAction(tr("Quit"), qApp, &QApplication::quit); - quitAction->setShortcut(QKeySequence(config->value<std::string>("browser.shortcuts.quit").value().c_str())); - - // Tools menu - QMenu *toolsMenu = new QMenu(tr("Tools"), this); - addMenu(toolsMenu); - - m_downloadsAction = toolsMenu->addAction(tr("Downloads")); - m_downloadsAction->setParent(parent); - m_downloadsAction->setShortcut(QKeySequence(config->value<std::string>("downloads.shortcut").value().c_str())); - connect(m_downloadsAction, &QAction::triggered, [parent]() { - parent->m_downloadsWidget->show(); - }); - - m_bookmarksAction = toolsMenu->addAction(tr("Bookmarks")); - m_bookmarksAction->setParent(parent); - m_bookmarksAction->setShortcut(QKeySequence(config->value<std::string>("bookmarks.shortcut").value().c_str())); - - //toolsMenu->addAction(tr("Filter"), browser->blocklists(), SLOT(show()), QKeySequence::fromString(browser->settings()->value("blocker.shortcut").toString())); - - // Profile menu - QMenu *profileMenu = addMenu(tr("Profile")); - m_profileAction = profileMenu->addAction(tr("Current profile")); - m_profileAction->setEnabled(false); - - QMenu *loadMenu = profileMenu->addMenu(tr("Load")); - - for(const QString &name : browser->profiles()) { - QAction *profileAction = loadMenu->addAction(browser->profile(name)->name()); - connect(profileAction, &QAction::triggered, this, [browser, parent, name] { - parent->setProfile(browser->profile(name)); - }); - } -} - -QAction *MainWindowMenuBar::bookmarksAction() -{ - Q_CHECK_PTR(m_bookmarksAction); - return m_bookmarksAction; -} - -QAction *MainWindowMenuBar::profileAction() const -{ - Q_CHECK_PTR(m_profileAction); - return m_profileAction; -} - diff --git a/src/widgets/mainwindowmenubar.h b/src/widgets/mainwindowmenubar.h deleted file mode 100644 index 11c8beb..0000000 --- a/src/widgets/mainwindowmenubar.h +++ /dev/null @@ -1,32 +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 MAINWINDOWMENUBAR_H -#define MAINWINDOWMENUBAR_H - -#include <QMenuBar> -#include <memory> - -class MainWindow; -class Configuration; -class MainWindowMenuBar : public QMenuBar -{ - Q_OBJECT -public: - explicit MainWindowMenuBar(std::shared_ptr<Configuration> config, MainWindow *parent = nullptr); - - QAction *bookmarksAction(); - QAction *profileAction() const; - -private: - QAction *m_bookmarksAction; - QAction *m_downloadsAction; - QAction *m_profileAction; -}; - -#endif // MAINWINDOWMENUBAR_H |