aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAqua-sama <aqua@iserlohn-fortress.net>2020-04-13 15:44:09 +0300
committerAqua-sama <aqua@iserlohn-fortress.net>2020-04-13 15:44:09 +0300
commit396bc0c1721af8d3ee970228e7df457f6b2c73d5 (patch)
tree5aee4f0faec3fdfe616e1684dcb1736be9126bc1
parentAdd singleapplication.wrap (diff)
downloadsmolbote-396bc0c1721af8d3ee970228e7df457f6b2c73d5.tar.xz
Rewrite Session backend
Add session.hpp, containing structs that describe session data MainWindow, SubWindow and WebView can be created from Session::structs Opening new window will automatically open a default subwindow and tab if none were specified Add lib/session_formats Add JsonSession, to serialize/deserialize Session structs into JSON - add some tests clang-tidy: - fix various warnings - disable modernize-use-trailing-return-type check
-rw-r--r--.clang-tidy2
-rw-r--r--include/session.hpp41
-rw-r--r--lib/session_formats/meson.build13
-rw-r--r--lib/session_formats/session_json.cpp97
-rw-r--r--lib/session_formats/session_json.hpp36
-rw-r--r--lib/session_formats/test/json.cpp103
-rw-r--r--linux/makepkg/PKGBUILD4
-rw-r--r--meson.build4
-rw-r--r--src/browser.cpp44
-rw-r--r--src/browser.h4
-rw-r--r--src/main.cpp42
-rw-r--r--src/mainwindow/mainwindow.cpp54
-rw-r--r--src/mainwindow/mainwindow.h16
-rw-r--r--src/mainwindow/menubar.cpp17
-rw-r--r--src/meson.build6
-rw-r--r--src/session/savesessiondialog.cpp12
-rw-r--r--src/session/savesessiondialog.h8
-rw-r--r--src/session/session.cpp177
-rw-r--r--src/session/session.h34
-rw-r--r--src/session/sessiondialog.cpp89
-rw-r--r--src/session/sessiondialog.h13
-rw-r--r--src/session/sessiondialog.ui12
-rw-r--r--src/session/sessionform.cpp36
-rw-r--r--src/session/sessionform.h37
-rw-r--r--src/session/sessionform.ui41
-rw-r--r--src/subwindow/subwindow.cpp37
-rw-r--r--src/subwindow/subwindow.h4
-rw-r--r--src/webengine/webview.cpp47
-rw-r--r--src/webengine/webview.h10
-rw-r--r--subprojects/catch2.wrap10
30 files changed, 590 insertions, 460 deletions
diff --git a/.clang-tidy b/.clang-tidy
index 5813ee3..4963ca4 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -1,5 +1,5 @@
---
-Checks: 'clang-diagnostic-*,clang-analyzer-*,bugprone-*,cert-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*'
+Checks: 'clang-diagnostic-*,clang-analyzer-*,bugprone-*,cert-*,cppcoreguidelines-*,hicpp-*,misc-*,modernize-*,performance-*,portability-*,readability-*, -modernize-use-trailing-return-type'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
diff --git a/include/session.hpp b/include/session.hpp
new file mode 100644
index 0000000..0146802
--- /dev/null
+++ b/include/session.hpp
@@ -0,0 +1,41 @@
+/*
+ * 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://library.iserlohn-fortress.net/aqua/smolbote.git
+ *
+ * SPDX-License-Identifier: MIT
+ */
+
+#ifndef SMOLBOTE_SESSION_HPP
+#define SMOLBOTE_SESSION_HPP
+
+#include <QByteArray>
+#include <QString>
+#include <QStringList>
+#include <QVector>
+
+class [[nodiscard]] Session
+{
+public:
+ struct WebView {
+ QString profile;
+ QString url;
+ QByteArray history;
+ };
+
+ struct SubWindow {
+ QString profile;
+ QVector<WebView> tabs;
+ };
+
+ struct MainWindow {
+ QVector<SubWindow> subwindows;
+ };
+
+ virtual ~Session() = default;
+
+ [[nodiscard]] virtual QByteArray serialize() const = 0;
+ [[nodiscard]] virtual QVector<MainWindow> get() const = 0;
+};
+
+#endif // SMOLBOTE_SESSION_HPP
diff --git a/lib/session_formats/meson.build b/lib/session_formats/meson.build
new file mode 100644
index 0000000..9abf5a2
--- /dev/null
+++ b/lib/session_formats/meson.build
@@ -0,0 +1,13 @@
+lib_session_formats = declare_dependency(
+ include_directories: [ '.', include ],
+ link_with: library('sessionformats',
+ [ 'session_json.cpp' ],
+ include_directories: include,
+ dependencies: dep_qt5
+ )
+)
+
+test('session: json format', executable('session_json',
+ sources: 'test/json.cpp',
+ dependencies: [ dep_qt5, dep_catch, lib_session_formats ]
+))
diff --git a/lib/session_formats/session_json.cpp b/lib/session_formats/session_json.cpp
new file mode 100644
index 0000000..eca96f2
--- /dev/null
+++ b/lib/session_formats/session_json.cpp
@@ -0,0 +1,97 @@
+/*
+ * 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://library.iserlohn-fortress.net/aqua/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#include "session_json.hpp"
+#include <QJsonArray>
+#include <QJsonDocument>
+
+[[nodiscard]] QVector<Session::MainWindow> toWindowList(const QString &profile, const QStringList &urls)
+{
+ Session::SubWindow main_;
+ main_.profile = profile;
+ for(const auto &url : urls) {
+ main_.tabs.append({ profile, url, QByteArray() });
+ }
+ Session::MainWindow main;
+ main.subwindows.append(main_);
+ return { main };
+}
+
+[[nodiscard]] QJsonObject toJson(const QVector<Session::MainWindow> &windows)
+{
+ QJsonObject root;
+ QJsonArray windows_;
+ {
+ for(const auto &window : windows) {
+ QJsonObject window_;
+ QJsonArray subwindows;
+ for(const auto &subwindow : window.subwindows) {
+ QJsonObject subwindow_json;
+ subwindow_json.insert("profile", subwindow.profile);
+ QJsonArray tabs;
+ for(const auto &tab : subwindow.tabs) {
+ QJsonObject tab_;
+ tab_.insert("profile", tab.profile);
+ tab_.insert("url", tab.url);
+ tab_.insert("history", QString::fromLatin1(tab.history.toBase64()));
+ tabs.append(tab_);
+ }
+ subwindow_json.insert("tabs", tabs);
+ subwindows.append(subwindow_json);
+ }
+ window_.insert("subwindows", subwindows);
+ windows_.append(window_);
+ }
+ }
+ root.insert("windows", windows_);
+ return root;
+}
+
+JsonSession::JsonSession(const QByteArray &data)
+ : root(QJsonDocument::fromJson(data).object())
+{
+}
+
+JsonSession::JsonSession(const QVector<MainWindow> &windows)
+ : root(toJson(windows))
+{
+}
+
+JsonSession::JsonSession(const QString &profile, const QStringList &urls)
+ : root(toJson(toWindowList(profile, urls)))
+{
+}
+
+QByteArray JsonSession::serialize() const
+{
+ QJsonDocument doc(root);
+ return doc.toJson(QJsonDocument::Compact);
+}
+
+QVector<Session::MainWindow> JsonSession::get() const
+{
+ QVector<Session::MainWindow> windows;
+ for(const auto &windowData : root["windows"].toArray()) {
+ Session::MainWindow window;
+ for(const auto &subwindowData : windowData.toObject()["subwindows"].toArray()) {
+ Session::SubWindow subwindow;
+ subwindow.profile = subwindowData.toObject()["profile"].toString();
+ for(const auto &tabData : subwindowData.toObject()["tabs"].toArray()) {
+ Session::WebView tab{
+ tabData.toObject()["profile"].toString(),
+ tabData.toObject()["url"].toString(),
+ QByteArray::fromBase64(tabData.toObject()["history"].toString().toLatin1())
+ };
+ subwindow.tabs.append(tab);
+ }
+ window.subwindows.append(subwindow);
+ }
+ windows.append(window);
+ }
+ return windows;
+}
diff --git a/lib/session_formats/session_json.hpp b/lib/session_formats/session_json.hpp
new file mode 100644
index 0000000..3332229
--- /dev/null
+++ b/lib/session_formats/session_json.hpp
@@ -0,0 +1,36 @@
+/*
+ * 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://library.iserlohn-fortress.net/aqua/smolbote
+ *
+ * SPDX-License-Identifier: GPL-3.0
+ */
+
+#ifndef SESSION_JSON_HPP
+#define SESSION_JSON_HPP
+
+#include <QJsonObject>
+#include <session.hpp>
+
+class JsonSession : public Session
+{
+public:
+ explicit JsonSession() = default;
+ JsonSession(const JsonSession &) = default;
+ JsonSession(JsonSession &&) = default;
+ JsonSession &operator=(const JsonSession &) = delete;
+ JsonSession &operator=(JsonSession &&) = delete;
+
+ explicit JsonSession(const QByteArray &data);
+ explicit JsonSession(const QVector<MainWindow> &windows);
+ JsonSession(const QString &profile, const QStringList &urls);
+ ~JsonSession() override = default;
+
+ [[nodiscard]] QByteArray serialize() const override;
+ [[nodiscard]] QVector<MainWindow> get() const override;
+
+private:
+ const QJsonObject root;
+};
+
+#endif // SESSION_JSON_HPP
diff --git a/lib/session_formats/test/json.cpp b/lib/session_formats/test/json.cpp
new file mode 100644
index 0000000..4c6b683
--- /dev/null
+++ b/lib/session_formats/test/json.cpp
@@ -0,0 +1,103 @@
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one cpp file
+#include <catch2/catch.hpp>
+#include <session_json.hpp>
+
+TEST_CASE("JsonSession default constructor")
+{
+ JsonSession session;
+ REQUIRE(session.serialize() == "{}");
+ const auto tree = session.get();
+ REQUIRE(tree.count() == 0);
+}
+
+TEST_CASE("JsonSession command line constructor, single URL")
+{
+ const QString profile = "";
+ const QString url = "https://some.url";
+ JsonSession session(profile, QStringList(url));
+
+ const auto tree = session.get();
+ REQUIRE(tree.count() == 1);
+
+ const auto window = tree.at(0);
+ REQUIRE(window.subwindows.count() == 1);
+
+ const auto subwindow = window.subwindows.at(0);
+ REQUIRE(subwindow.profile == profile);
+ REQUIRE(subwindow.tabs.count() == 1);
+
+ const auto tab = subwindow.tabs.at(0);
+ REQUIRE(tab.profile == profile);
+ REQUIRE(tab.url == url);
+ REQUIRE(tab.history.isEmpty());
+}
+
+TEST_CASE("JsonSession command line constructor, multiple URLs")
+{
+ const QString profile = "default";
+ const QStringList urls{ "https://some.url", "http://other.url", "a random string" };
+ JsonSession session(profile, urls);
+
+ const auto tree = session.get();
+ REQUIRE(tree.count() == 1);
+
+ const auto window = tree.at(0);
+ REQUIRE(window.subwindows.count() == 1);
+
+ const auto subwindow = window.subwindows.at(0);
+ REQUIRE(subwindow.profile == profile);
+ REQUIRE(subwindow.tabs.count() == 3);
+
+ for(int i = 0; i < urls.size(); ++i) {
+ const auto tab = subwindow.tabs.at(i);
+ REQUIRE(tab.profile == profile);
+ REQUIRE(tab.url == urls.at(i));
+ REQUIRE(tab.history.isEmpty());
+ }
+}
+
+TEST_CASE("JsonSession QByteArray constuctor")
+{
+ const QString profile = "";
+ const QString url = "about:blank";
+ JsonSession donor(profile, { url });
+ JsonSession session(donor.serialize());
+
+ const auto tree = session.get();
+ REQUIRE(tree.count() == 1);
+
+ const auto window = tree.at(0);
+ REQUIRE(window.subwindows.count() == 1);
+
+ const auto subwindow = window.subwindows.at(0);
+ REQUIRE(subwindow.profile == profile);
+ REQUIRE(subwindow.tabs.count() == 1);
+
+ const auto tab = subwindow.tabs.at(0);
+ REQUIRE(tab.profile == profile);
+ REQUIRE(tab.url == url);
+ REQUIRE(tab.history.isEmpty());
+}
+
+TEST_CASE("JsonSession MainWindowVector constructor")
+{
+ const Session::WebView cview{ "profile", "url", "history" };
+ const Session::SubWindow csubwindow{ "profile", { cview } };
+ const Session::MainWindow cwindow{ { csubwindow } };
+
+ const JsonSession session{ { cwindow } };
+ const auto tree = session.get();
+ REQUIRE(tree.count() == 1);
+
+ const auto window = tree.at(0);
+ REQUIRE(window.subwindows.count() == 1);
+
+ const auto subwindow = window.subwindows.at(0);
+ REQUIRE(subwindow.profile == csubwindow.profile);
+ REQUIRE(subwindow.tabs.count() == 1);
+
+ const auto tab = subwindow.tabs.at(0);
+ REQUIRE(tab.profile == cview.profile);
+ REQUIRE(tab.url == cview.url);
+ REQUIRE(tab.history == cview.history);
+}
diff --git a/linux/makepkg/PKGBUILD b/linux/makepkg/PKGBUILD
index 78549c3..b7086ad 100644
--- a/linux/makepkg/PKGBUILD
+++ b/linux/makepkg/PKGBUILD
@@ -20,7 +20,7 @@ arch=('x86_64' 'aarch64')
license=('GPL3')
depends=('qt5-webengine>=5.11.0' 'spdlog')
-makedepends=('git' 'meson' 'python-kconfiglib' 'openssl' 'qt5-tools' 'scdoc')
+makedepends=('git' 'meson' 'python-kconfiglib' 'openssl' 'qt5-tools' 'scdoc' 'catch2')
if [ $_enableBreakpad == "1" ]; then
makedepends+=('breakpad-git')
fi
@@ -76,7 +76,7 @@ build() {
# b_lto: Use link time optimization
meson --buildtype=release --wrap-mode=nodownload \
--prefix=$_prefix --auto-features=disabled \
- -Db_pie=true \
+ -Db_pie=true -Ddefault_library=static \
-Dmanpage=enabled \
$srcdir/build
diff --git a/meson.build b/meson.build
index 003d312..62d517d 100644
--- a/meson.build
+++ b/meson.build
@@ -65,6 +65,7 @@ if dep_breakpad.found()
endif
dep_gtest = dependency('gtest', required: false, disabler: true)
+dep_catch = dependency('catch2', required: true, fallback: ['catch2', 'catch2_dep'] )
dep_SingleApplication = dependency('singleapplication', fallback: [ 'singleapplication', 'SingleApplication_dep' ])
# Generate config header
@@ -80,6 +81,7 @@ subdir('lib/configuration')
subdir('lib/downloads')
subdir('lib/pluginloader')
subdir('lib/urlfilter')
+subdir('lib/session_formats')
subdir('3rd-party/args')
@@ -99,7 +101,7 @@ poi_exe = executable(get_option('poi'),
cpp_args: ['-DQAPPLICATION_CLASS=QApplication', poi_cpp_args],
sources: [ssconfig.sources()],
include_directories: [include, include_directories('src')],
- dependencies: [ dep_qt5, dep_spdlog, dep_SingleApplication, dep_args, optional_deps, dep_about, dep_bookmarks, dep_configuration, dep_downloads, dep_pluginloader, dep_urlfilter, dep_plugininterface, ssconfig.dependencies() ],
+ dependencies: [ dep_qt5, dep_spdlog, dep_SingleApplication, dep_args, optional_deps, dep_about, dep_bookmarks, dep_configuration, dep_downloads, dep_pluginloader, dep_urlfilter, dep_plugininterface, ssconfig.dependencies(), lib_session_formats ],
install: true,
)
diff --git a/src/browser.cpp b/src/browser.cpp
index 02d5bc9..3c39ec7 100644
--- a/src/browser.cpp
+++ b/src/browser.cpp
@@ -218,22 +218,44 @@ void Browser::showWidget(QWidget *widget, MainWindow *where) const
where->addDockWidget(Qt::RightDockWidgetArea, widget);
}
-MainWindow *Browser::createWindow()
+void Browser::open(const QVector<Session::MainWindow> &data, bool merge)
{
- // the window will delete itself when it closes, so we don't need to delete it
- auto *window = new MainWindow();
- connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search);
+ if(data.count() == 0 && merge) {
+ m_windows.at(0)->createTab(QUrl());
+ }
+
+ if(data.count() == 1 && m_windows.count() >= 1 && merge) {
+ const auto windowData = data.at(0);
+ auto *window = m_windows.at(0);
+
+ if(windowData.subwindows.count() == 1) {
+ const auto subwindowData = windowData.subwindows.at(0);
+ for(const auto &tab : subwindowData.tabs) {
+ window->createTab(tab);
+ }
+ } else {
+ for(const auto &data : windowData.subwindows) {
+ window->createSubWindow(data);
+ }
+ }
- for(auto *info : qAsConst(m_plugins)) {
- addPluginTo(info, window);
+ return;
}
- m_windows.append(window);
- connect(window, &MainWindow::destroyed, this, [this, window]() {
- m_windows.removeOne(window);
- });
+ for(const auto &windowData : data) {
+ // the window will delete itself when it closes, so we don't need to delete it
+ auto *window = new MainWindow(windowData);
+ connect(window->addressBar, &AddressBar::complete, m_bookmarks.get(), &BookmarksWidget::search);
- return window;
+ for(auto *info : qAsConst(m_plugins)) {
+ addPluginTo(info, window);
+ }
+
+ m_windows.append(window);
+ connect(window, &MainWindow::destroyed, this, [this, window]() {
+ m_windows.removeOne(window);
+ });
+ }
}
void Browser::addPluginTo(PluginInfo *info, MainWindow *window)
diff --git a/src/browser.h b/src/browser.h
index 646d57e..95ce936 100644
--- a/src/browser.h
+++ b/src/browser.h
@@ -9,7 +9,7 @@
#ifndef SMOLBOTE_BROWSER_H
#define SMOLBOTE_BROWSER_H
-#include "session/session.h"
+#include "session.hpp"
#include <QJsonObject>
#include <QMenu>
#include <QPluginLoader>
@@ -60,7 +60,7 @@ public:
public slots:
void showWidget(QWidget *widget, MainWindow *where) const;
- MainWindow *createWindow();
+ void open(const QVector<Session::MainWindow> &data, bool merge = true);
private:
struct PluginInfo {
diff --git a/src/main.cpp b/src/main.cpp
index e04f58a..9ceda77 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,8 +10,8 @@
#include "builtins.h"
#include "configuration.h"
#include "crashhandler.h"
-#include "session/session.h"
#include "session/sessiondialog.h"
+#include "session_json.hpp"
#include "settings.h"
#include "util.h"
#include "version.h"
@@ -145,37 +145,35 @@ int main(int argc, char **argv)
// if app is primary, create new sessions from received messages
if(app.isPrimary() && !cmd_noRemote) {
- QObject::connect(&app, &Browser::receivedMessage, &app, [](quint32 instanceId, QByteArray message) {
+ QObject::connect(&app, &Browser::receivedMessage, &app, [&app](quint32 instanceId, QByteArray message) {
Q_UNUSED(instanceId);
- auto doc = QJsonDocument::fromJson(message);
- Session::restoreSession(doc.object());
+ JsonSession session(message);
+ app.open(session.get());
});
}
{
- QJsonObject sessionData;
-
- if(cmd_pickSession) {
- auto *dlg = new SessionDialog();
- if(const auto pick = dlg->pickSession())
- sessionData = pick.value();
- else
- sessionData = Session::fromCommandLine(profile, urls);
- } else if(cmd_session) {
- QFile sessionJson(QString::fromStdString(args::get(cmd_session)));
- if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) {
- sessionData = QJsonDocument::fromJson(sessionJson.readAll()).object();
- sessionJson.close();
+ const auto session = [&]() {
+ if(cmd_session) {
+ QFile sessionJson(QString::fromStdString(args::get(cmd_session)));
+ if(sessionJson.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return JsonSession(sessionJson.readAll());
+ }
}
- } else {
- sessionData = Session::fromCommandLine(profile, urls);
- }
+ if(cmd_pickSession) {
+ SessionDialog dlg;
+ if(const auto pick = dlg.pickSession()) {
+ return JsonSession(pick.value());
+ }
+ }
+ return JsonSession(profile, urls);
+ }();
if(app.isPrimary() || cmd_noRemote) {
- Session::restoreSession(sessionData);
+ app.open(session.get());
} else {
// app is secondary and not standalone
- return app.sendMessage(QJsonDocument(sessionData).toJson());
+ return app.sendMessage(session.serialize());
}
}
diff --git a/src/mainwindow/mainwindow.cpp b/src/mainwindow/mainwindow.cpp
index b607a63..e16f34f 100644
--- a/src/mainwindow/mainwindow.cpp
+++ b/src/mainwindow/mainwindow.cpp
@@ -14,6 +14,7 @@
#include "configuration.h"
#include "menubar.h"
#include "webengine/webprofile.h"
+#include "webengine/webprofilemanager.h"
#include "webengine/webview.h"
#include "widgets/dockwidget.h"
#include "widgets/navigationbar.h"
@@ -23,10 +24,9 @@
#include <QMessageBox>
#include <QStatusBar>
-MainWindow::MainWindow(QWidget *parent)
+MainWindow::MainWindow(const Session::MainWindow &data, QWidget *parent)
: QMainWindow(parent)
{
-
Configuration config;
// create UI
@@ -46,7 +46,7 @@ MainWindow::MainWindow(QWidget *parent)
addressBar = new AddressBar(this);
navigationToolBar->addWidget(addressBar);
- Browser *app = qobject_cast<Browser *>(qApp);
+ auto *app = qobject_cast<Browser *>(qApp);
this->addToolBarBreak();
this->addToolBar(new BookmarksToolbar(app->bookmarks()->model(), this));
@@ -65,6 +65,14 @@ MainWindow::MainWindow(QWidget *parent)
searchBox->setVisible(!searchBox->isVisible());
});
QMainWindow::addAction(searchAction);
+
+ for(const auto &s : data.subwindows) {
+ createSubWindow(s);
+ }
+ if(m_subwindows.isEmpty()) {
+ const Session::SubWindow s;
+ createSubWindow(s);
+ }
}
void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget)
@@ -72,18 +80,20 @@ void MainWindow::addDockWidget(Qt::DockWidgetArea area, QWidget *widget)
QDockWidget *lastDock = nullptr;
const auto docks = findChildren<QDockWidget *>();
for(QDockWidget *dock : docks) {
- if(dockWidgetArea(dock) == area)
+ if(dockWidgetArea(dock) == area) {
lastDock = dock;
+ }
}
- DockWidget *dock = new DockWidget(widget->windowTitle(), this);
+ auto *dock = new DockWidget(widget->windowTitle(), this);
dock->setMinimumWidth(460);
dock->setWidget(widget);
- if(lastDock == nullptr)
+ if(lastDock == nullptr) {
QMainWindow::addDockWidget(area, dock);
- else
+ } else {
tabifyDockWidget(lastDock, dock);
+ }
}
void MainWindow::removeDockWidget(QWidget *widget)
@@ -105,8 +115,23 @@ void MainWindow::createTab(const QUrl &url)
}
}
-SubWindow *MainWindow::createSubWindow(WebProfile *profile, bool openProfileNewtab)
+void MainWindow::createTab(const Session::WebView &data)
+{
+ auto *w = qobject_cast<SubWindow *>(centralWidget());
+ if(w != nullptr) {
+ w->addTab(data);
+ }
+}
+
+SubWindow *MainWindow::createSubWindow(const Session::SubWindow &data)
{
+ const auto *profileManager = WebProfileManager::instance();
+ Q_CHECK_PTR(profileManager);
+
+ auto *profile = profileManager->profile(data.profile);
+ if(profile == nullptr) {
+ profile = WebProfile::defaultProfile();
+ }
auto *w = new SubWindow(this);
w->setProfile(profile);
m_subwindows.append(w);
@@ -114,8 +139,9 @@ SubWindow *MainWindow::createSubWindow(WebProfile *profile, bool openProfileNewt
m_menuBar->insertSubWindow(w);
connect(w, &SubWindow::windowTitleChanged, this, [this, w](const QString &title) {
- if(w == currentSubWindow())
+ if(w == currentSubWindow()) {
setWindowTitle(QString("[%1] - %2").arg(title, defaultWindowTitle));
+ }
});
connect(w, &SubWindow::aboutToClose, this, [this, w]() {
@@ -128,9 +154,13 @@ SubWindow *MainWindow::createSubWindow(WebProfile *profile, bool openProfileNewt
}
});
- if(openProfileNewtab)
- w->addTab(w->profile()->newtab());
-
+ if(data.tabs.count() == 0) {
+ w->addTab(profile->newtab());
+ return w;
+ }
+ for(const auto &tab : data.tabs) {
+ w->addTab(tab);
+ }
return w;
}
diff --git a/src/mainwindow/mainwindow.h b/src/mainwindow/mainwindow.h
index 98bd6ee..2b9f82b 100644
--- a/src/mainwindow/mainwindow.h
+++ b/src/mainwindow/mainwindow.h
@@ -9,7 +9,10 @@
#ifndef SMOLBOTE_MAINWINDOW_H
#define SMOLBOTE_MAINWINDOW_H
+#include "session.hpp"
#include "subwindow/subwindow.h"
+#include <QJsonArray>
+#include <QJsonObject>
#include <QMainWindow>
#include <QUrl>
@@ -31,9 +34,17 @@ public:
ToolsMenu
};
- explicit MainWindow(QWidget *parent = nullptr);
+ explicit MainWindow(const Session::MainWindow &data, QWidget *parent = nullptr);
MainWindow(const MainWindow &) = delete;
~MainWindow() = default;
+ [[nodiscard]] Session::MainWindow serialize() const
+ {
+ QVector<Session::SubWindow> subwindows(m_subwindows.size());
+ for(int i = 0; i < m_subwindows.size(); ++i) {
+ subwindows[i] = m_subwindows.at(i)->serialize();
+ }
+ return { subwindows };
+ }
void addDockWidget(Qt::DockWidgetArea area, QWidget *widget);
void removeDockWidget(QWidget *widget);
@@ -53,7 +64,8 @@ public:
public slots:
void createTab(const QUrl &url);
- SubWindow *createSubWindow(WebProfile *profile = nullptr, bool openProfileNewtab = false);
+ void createTab(const Session::WebView &data);
+ SubWindow *createSubWindow(const Session::SubWindow &data);
void setCurrentSubWindow(SubWindow *subwindow);
private slots:
diff --git a/src/mainwindow/menubar.cpp b/src/mainwindow/menubar.cpp
index c0bda61..83cafee 100644
--- a/src/mainwindow/menubar.cpp
+++ b/src/mainwindow/menubar.cpp
@@ -33,14 +33,16 @@
inline void run_if(SubWindow *_subwindow, const std::function<void(SubWindow *, int)> &f)
{
- if(_subwindow != nullptr)
+ if(_subwindow != nullptr) {
f(_subwindow, _subwindow->currentTabIndex());
+ }
}
inline void trigger_if(WebView *_view, QWebEnginePage::WebAction action)
{
- if(_view != nullptr)
+ if(_view != nullptr) {
_view->triggerPageAction(action);
+ }
}
inline QDialog *createDevToolsDialog(QWebEnginePage *page)
@@ -88,8 +90,9 @@ MenuBar::MenuBar(MainWindow *parent)
connect(findMenu, &QMenu::aboutToShow, [findMenu, find_lineEdit]() {
find_lineEdit->clear();
const auto actions = findMenu->actions();
- for(int i = 1; i < actions.length(); i++)
+ for(int i = 1; i < actions.length(); i++) {
findMenu->removeAction(actions.at(i));
+ }
find_lineEdit->setFocus();
});
@@ -155,11 +158,15 @@ MenuBar::MenuBar(MainWindow *parent)
window = this->addMenu(tr("&Window"));
{
- auto *actionNewWindow = window->addAction(tr("New Window"), browser, &Browser::createWindow);
+ auto *actionNewWindow = window->addAction(tr("New Window"), browser, [browser]() {
+ const Session::MainWindow window;
+ browser->open({ window }, false);
+ });
conf.shortcut<QAction>(*actionNewWindow, "shortcuts.window.newwindow");
auto *actionNewSubwindow = window->addAction(tr("New Subwindow"), parent, [parent]() {
- parent->createSubWindow(nullptr, true);
+ const Session::SubWindow session;
+ parent->createSubWindow(session);
});
conf.shortcut<QAction>(*actionNewSubwindow, "shortcuts.window.newgroup");
diff --git a/src/meson.build b/src/meson.build
index edc9db4..31f4ffc 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -11,13 +11,13 @@ poi_sourceset.add(mod_qt5.preprocess(
moc_headers: ['browser.h',
'mainwindow/mainwindow.h', 'mainwindow/addressbar.h', 'mainwindow/menubar.h', 'mainwindow/widgets/completer.h', 'mainwindow/widgets/urllineedit.h', 'mainwindow/widgets/dockwidget.h', 'mainwindow/widgets/navigationbar.h', 'mainwindow/widgets/searchform.h',
'bookmarks/bookmarkswidget.h', 'bookmarks/editbookmarkdialog.h',
- 'session/savesessiondialog.h', 'session/sessiondialog.h', 'session/sessionform.h',
+ 'session/savesessiondialog.h', 'session/sessiondialog.h',
'subwindow/subwindow.h', 'subwindow/tabwidget.h',
'webengine/urlinterceptor.h', 'webengine/webpage.h', 'webengine/webview.h', 'webengine/webprofilemanager.h', 'webengine/webprofile.h'],
ui_files: [
'mainwindow/addressbar.ui', 'mainwindow/widgets/searchform.ui',
'bookmarks/bookmarksform.ui', 'bookmarks/editbookmarkdialog.ui',
- 'session/savesessiondialog.ui', 'session/sessiondialog.ui', 'session/sessionform.ui'],
+ 'session/savesessiondialog.ui', 'session/sessiondialog.ui' ],
qresources: '../data/resources.qrc',
rcc_extra_arguments: ['--format-version=1'],
dependencies: dep_qt5
@@ -40,10 +40,8 @@ poi_sourceset.add(files(
'bookmarks/builtins.cpp', 'bookmarks/bookmarkswidget.cpp', 'bookmarks/editbookmarkdialog.cpp', 'bookmarks/bookmarkstoolbar.cpp',
- 'session/session.cpp',
'session/savesessiondialog.cpp',
'session/sessiondialog.cpp',
- 'session/sessionform.cpp',
'subwindow/subwindow.cpp',
'subwindow/tabwidget.cpp',
diff --git a/src/session/savesessiondialog.cpp b/src/session/savesessiondialog.cpp
index 507cddb..ff5e696 100644
--- a/src/session/savesessiondialog.cpp
+++ b/src/session/savesessiondialog.cpp
@@ -1,7 +1,7 @@
/*
* 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/gitea/aqua/smolbote
+ * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -9,6 +9,7 @@
#include "savesessiondialog.h"
#include "browser.h"
#include "mainwindow/mainwindow.h"
+#include "session_json.hpp"
#include "subwindow/subwindow.h"
#include "ui_savesessiondialog.h"
#include "webengine/webprofilemanager.h"
@@ -61,18 +62,17 @@ void SaveSessionDialog::save(const QString &sessionPath)
QFile output(filename);
if(output.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) {
- QVector<MainWindow *> windows;
+ QVector<Session::MainWindow> windows;
for(int i = 0; i < ui->treeWidget->topLevelItemCount(); ++i) {
QTreeWidgetItem *item = ui->treeWidget->topLevelItem(i);
if(item->checkState(0) == Qt::Checked) {
auto *window = static_cast<MainWindow *>(item->data(0, Qt::UserRole).value<void *>());
- Q_CHECK_PTR(window);
- windows.append(window);
+ windows.append(window->serialize());
}
}
- auto data = Session::_session(windows);
- output.write(QJsonDocument(data).toJson());
+ JsonSession session(windows);
+ output.write(session.serialize());
output.close();
}
}
diff --git a/src/session/savesessiondialog.h b/src/session/savesessiondialog.h
index ade1d23..8a162d1 100644
--- a/src/session/savesessiondialog.h
+++ b/src/session/savesessiondialog.h
@@ -1,7 +1,7 @@
/*
* 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/gitea/aqua/smolbote
+ * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -22,7 +22,11 @@ class SaveSessionDialog : public QDialog
public:
explicit SaveSessionDialog(QWidget *parent = nullptr);
- ~SaveSessionDialog();
+ explicit SaveSessionDialog(const SaveSessionDialog &) = delete;
+ explicit SaveSessionDialog(SaveSessionDialog &&) = delete;
+ SaveSessionDialog &operator=(const SaveSessionDialog &) = delete;
+ SaveSessionDialog &operator=(SaveSessionDialog &&) = delete;
+ ~SaveSessionDialog() override;
public slots:
void save(const QString &sessionPath);
diff --git a/src/session/session.cpp b/src/session/session.cpp
deleted file mode 100644
index c97c22b..0000000
--- a/src/session/session.cpp
+++ /dev/null
@@ -1,177 +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/gitea/aqua/smolbote
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#include "session.h"
-#include "../webengine/webview.h"
-#include "browser.h"
-#include "configuration.h"
-#include "mainwindow/mainwindow.h"
-#include "subwindow/subwindow.h"
-#include "webengine/webprofilemanager.h"
-#include "webengine/webview.h"
-#include <QJsonArray>
-#include <QJsonObject>
-#include <QWebEngineHistory>
-#include <memory>
-
-QJsonObject Session::fromCommandLine(const QString &profile, const QStringList &urls)
-{
- QJsonObject session;
-
- QJsonArray subwindows;
- {
- QJsonObject window;
- window.insert("profile", profile);
-
- QJsonArray tabs;
- for(const auto &url : urls) {
- QJsonObject tab;
- tab.insert("url", url);
- tab.insert("profile", profile);
- tabs.append(tab);
- }
- window.insert("tabs", tabs);
-
- subwindows.append(window);
- }
- session.insert("subwindows", subwindows);
-
- return session;
-}
-
-//////
-
-QJsonObject Session::_session(const QVector<MainWindow *> windows)
-{
- QJsonObject sessionData;
-
- {
- QJsonArray windowList;
- for(const auto *window : windows) {
- windowList.append(Session::_window(window));
- }
- sessionData.insert("windows", windowList);
- }
-
- return sessionData;
-}
-
-QJsonObject Session::_window(const MainWindow *window)
-{
- QJsonObject windowData;
-
- {
- QJsonArray subwindows;
- for(const auto *subwindow : window->subWindows()) {
- subwindows.append(Session::_subwindow(subwindow));
- }
- windowData.insert("subwindows", subwindows);
- }
-
- return windowData;
-}
-
-QJsonObject Session::_subwindow(const SubWindow *subwindow)
-{
- const auto *profileManager = WebProfileManager::instance();
- Q_CHECK_PTR(profileManager);
-
- QJsonObject subwindowData;
-
- subwindowData.insert("profile", profileManager->id(subwindow->profile()));
-
- {
- QJsonArray tabs;
- for(int i = 0; i < subwindow->tabCount(); ++i) {
- tabs.append(Session::view(subwindow->view(i)));
- }
- subwindowData.insert("tabs", tabs);
- }
-
- return subwindowData;
-}
-
-QJsonObject Session::view(const WebView *view)
-{
- const auto *profileManager = WebProfileManager::instance();
- Q_CHECK_PTR(profileManager);
-
- QByteArray historyData;
- QDataStream historyStream(&historyData, QIODevice::WriteOnly);
- historyStream << *view->history();
-
- QJsonObject viewData;
- viewData.insert("profile", profileManager->id(view->profile()));
-
- // store history: compress, toBase64 (to make printable), toQString (to store in json)
- viewData.insert("history", QString(qCompress(historyData).toBase64()));
-
- return viewData;
-}
-
-void Session::restoreView(WebView *view, const QJsonObject &data)
-{
- const auto *profileManager = WebProfileManager::instance();
- Q_CHECK_PTR(profileManager);
-
- auto *profile = profileManager->profile(data["profile"].toString());
- if(profile != nullptr)
- view->setProfile(profile);
-
- auto url = data.value("url");
- if(url.isString())
- view->load(QUrl::fromUserInput(url.toString()));
- else {
- // restore history: toLatin1 (turn into bytearray), fromBase64, uncompress
- QByteArray historyData = qUncompress(QByteArray::fromBase64(data["history"].toString().toLatin1()));
- QDataStream historyStream(&historyData, QIODevice::ReadOnly);
- historyStream >> *view->history();
- }
-}
-
-void Session::restoreSession(const QJsonObject &sessionData)
-{
- auto *browser = dynamic_cast<Browser *>(qApp);
- Q_CHECK_PTR(browser);
- const auto *profileManager = WebProfileManager::instance();
- Q_CHECK_PTR(profileManager);
-
- for(const auto subwindowData : sessionData["subwindows"].toArray()) {
- MainWindow *window = nullptr;
- SubWindow *subwindow = nullptr;
-
- if(!browser->windows().isEmpty()) {
- window = browser->windows().last();
- subwindow = window->currentSubWindow();
- } else {
- window = browser->createWindow();
- subwindow = window->createSubWindow(profileManager->profile(subwindowData.toObject()["profile"].toString()));
- }
-
- Q_CHECK_PTR(window);
- Q_CHECK_PTR(subwindow);
-
- for(const auto tabData : subwindowData.toObject()["tabs"].toArray()) {
- auto *view = subwindow->view(subwindow->addTab());
- Session::restoreView(view, tabData.toObject());
- }
- }
-
- for(const auto windowData : sessionData["windows"].toArray()) {
- auto *window = browser->createWindow();
-
- for(const auto subwindowData : windowData.toObject()["subwindows"].toArray()) {
- auto *subwindow = window->createSubWindow(profileManager->profile(subwindowData.toObject()["profile"].toString()));
-
- for(const auto tabData : subwindowData.toObject()["tabs"].toArray()) {
- auto *view = subwindow->view(subwindow->addTab());
- Session::restoreView(view, tabData.toObject());
- }
- }
- }
-}
diff --git a/src/session/session.h b/src/session/session.h
deleted file mode 100644
index 8a96bb2..0000000
--- a/src/session/session.h
+++ /dev/null
@@ -1,34 +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/gitea/aqua/smolbote
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#ifndef SMOLBOTE_SESSION_H
-#define SMOLBOTE_SESSION_H
-
-#include <QJsonDocument>
-
-class MainWindow;
-class SubWindow;
-class WebView;
-
-namespace Session {
-QJsonObject fromCommandLine(const QString &profile, const QStringList &urls);
-
-// TODO:
-QJsonObject _session(const QVector<MainWindow *> windows);
-QJsonObject _window(const MainWindow *window);
-QJsonObject _subwindow(const SubWindow *subwindow);
-
-
-QJsonObject view(const WebView *view);
-void restoreView(WebView *view, const QJsonObject &data);
-
-void restoreSession(const QJsonObject &sessionData);
-
-} // namespace Session
-
-#endif // SMOLBOTE_SESSION_H
diff --git a/src/session/sessiondialog.cpp b/src/session/sessiondialog.cpp
index 301b4b6..36cf9cb 100644
--- a/src/session/sessiondialog.cpp
+++ b/src/session/sessiondialog.cpp
@@ -1,7 +1,7 @@
/*
* 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/gitea/aqua/smolbote
+ * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -9,56 +9,59 @@
#include "sessiondialog.h"
#include "../browser.h"
#include "../util.h"
-#include "sessionform.h"
+#include "configuration.h"
+#include "session_json.hpp"
#include "ui_sessiondialog.h"
-#include "ui_sessionform.h"
#include <QFile>
#include <QFileDialog>
-#include <QToolButton>
#include <QStyle>
-#include "configuration.h"
+#include <QToolButton>
SessionDialog::SessionDialog(QWidget *parent)
: QDialog(parent)
+ , create_window_str(QObject::tr("Create a new window"))
, ui(new Ui::SessionDialog)
{
ui->setupUi(this);
- ui->open->setIcon(style()->standardIcon(QStyle::SP_DirOpenIcon));
auto *browser = qobject_cast<Browser *>(qApp);
Q_CHECK_PTR(browser);
Configuration conf;
for(const QString &path : Util::files(conf.value<QString>("session.path").value(), { "*.json" })) {
- addItem(path);
+ ui->listWidget->addItem(path);
}
- ui->listWidget->addItem(tr("Create a new window"));
+ ui->listWidget->addItem(create_window_str);
- connect(ui->listWidget, &QListWidget::currentItemChanged, this, [this](QListWidgetItem *currentItem, QListWidgetItem *previousItem) {
- auto *currentWidget = qobject_cast<SessionForm *>(ui->listWidget->itemWidget(currentItem));
- if(currentWidget != nullptr)
- currentWidget->ui->delete_toolButton->show();
-
- auto *previousWidget = qobject_cast<SessionForm *>(ui->listWidget->itemWidget(previousItem));
- if(previousWidget != nullptr)
- previousWidget->ui->delete_toolButton->hide();
+ connect(ui->listWidget, &QListWidget::currentItemChanged, this, [this](QListWidgetItem *currentItem, QListWidgetItem * /*previousItem*/) {
+ ui->delete_toolButton->setEnabled(currentItem->text() != create_window_str);
});
- connect(ui->open, &QPushButton::clicked, this, [this]() {
+ connect(ui->open_toolButton, &QToolButton::clicked, this, [this]() {
const QString filename = QFileDialog::getOpenFileName(this, tr("Select Session file"), QDir::homePath(), tr("JSON (*.json)"));
if(!filename.isEmpty()) {
- ui->listWidget->setCurrentItem(addItem(filename));
+ ui->listWidget->addItem(filename);
+ ui->listWidget->setCurrentRow(ui->listWidget->count() - 1);
accept();
}
});
+ connect(ui->delete_toolButton, &QToolButton::clicked, this, [this]() {
+ const auto path = ui->listWidget->currentItem()->text();
+ if(path != create_window_str) {
+ QFile(path).remove();
+ delete ui->listWidget->currentItem();
+ }
+ });
+
accepted_connection = connect(this, &SessionDialog::accepted, this, [this, browser]() {
- auto *currentWidget = qobject_cast<SessionForm *>(ui->listWidget->itemWidget(ui->listWidget->currentItem()));
- if(currentWidget != nullptr)
- this->openSession(currentWidget->ui->label->text());
- else
- browser->createWindow();
+ const auto path = ui->listWidget->currentItem()->text();
+ if(path != create_window_str) {
+ openSession(path);
+ } else {
+ browser->open({});
+ }
});
connect(ui->search, &QLineEdit::textEdited, this, &SessionDialog::search);
@@ -69,7 +72,7 @@ SessionDialog::~SessionDialog()
delete ui;
}
-std::optional<QJsonObject> SessionDialog::pickSession()
+std::optional<QVector<Session::MainWindow>> SessionDialog::pickSession()
{
disconnect(accepted_connection);
@@ -77,52 +80,32 @@ std::optional<QJsonObject> SessionDialog::pickSession()
return std::nullopt;
}
- auto *currentWidget = qobject_cast<SessionForm *>(ui->listWidget->itemWidget(ui->listWidget->currentItem()));
- if(currentWidget != nullptr) {
- QFile json(currentWidget->ui->label->text());
+ const auto path = ui->listWidget->currentItem()->text();
+ if(path != create_window_str) {
+ QFile json(path);
if(json.open(QIODevice::ReadOnly | QIODevice::Text)) {
- auto doc = QJsonDocument::fromJson(json.readAll());
- return doc.object();
+ JsonSession session(json.readAll());
+ return session.get();
}
}
return std::nullopt;
}
-QListWidgetItem *SessionDialog::addItem(const QString &path)
-{
- auto *item = new QListWidgetItem(ui->listWidget);
- auto *widget = new SessionForm(path, this);
- connect(widget->ui->delete_toolButton, &QToolButton::clicked, this, [item, widget]() {
- delete item;
- delete widget;
- });
- ui->listWidget->setItemWidget(item, widget);
- return item;
-}
-
void SessionDialog::search(const QString &text)
{
for(int i = 0; i < ui->listWidget->count(); ++i) {
auto *item = ui->listWidget->item(i);
- auto *widget = qobject_cast<SessionForm *>(ui->listWidget->itemWidget(item));
-
- if(widget == nullptr)
- item->setHidden(!text.isEmpty());
- else
- item->setHidden(!widget->ui->label->text().contains(text));
+ item->setHidden(!item->text().contains(text));
}
}
void SessionDialog::openSession(const QString &filename)
{
auto *browser = qobject_cast<Browser *>(qApp);
- Q_CHECK_PTR(browser);
-
QFile json(filename);
- if(json.open(QIODevice::ReadOnly | QIODevice::Text)) {
- auto doc = QJsonDocument::fromJson(json.readAll());
- Session::restoreSession(doc.object());
- json.close();
+ if(json.open(QIODevice::ReadOnly | QIODevice::Text) && browser != nullptr) {
+ JsonSession session(json.readAll());
+ browser->open(session.get());
}
}
diff --git a/src/session/sessiondialog.h b/src/session/sessiondialog.h
index 9bf8a68..0a04940 100644
--- a/src/session/sessiondialog.h
+++ b/src/session/sessiondialog.h
@@ -1,7 +1,7 @@
/*
* 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/gitea/aqua/smolbote
+ * location: https://library.iserlohn-fortress.net/aqua/smolbote.git
*
* SPDX-License-Identifier: GPL-3.0
*/
@@ -9,6 +9,7 @@
#ifndef SMOLBOTE_SESSIONDIALOG_H
#define SMOLBOTE_SESSIONDIALOG_H
+#include "session.hpp"
#include <QDialog>
namespace Ui
@@ -22,16 +23,20 @@ class SessionDialog : public QDialog
public:
explicit SessionDialog(QWidget *parent = nullptr);
+ explicit SessionDialog(const SessionDialog &) = delete;
+ explicit SessionDialog(SessionDialog &&) = delete;
+ SessionDialog &operator=(const SessionDialog &) = delete;
+ SessionDialog &operator=(SessionDialog &&) = delete;
~SessionDialog() override;
- std::optional<QJsonObject> pickSession();
+ std::optional<QVector<Session::MainWindow>> pickSession();
private slots:
- QListWidgetItem *addItem(const QString &path);
void search(const QString &text);
- void openSession(const QString &filename);
+ static void openSession(const QString &filename);
private:
+ const QString create_window_str;
Ui::SessionDialog *ui;
QMetaObject::Connection accepted_connection;
};
diff --git a/src/session/sessiondialog.ui b/src/session/sessiondialog.ui
index dab49d9..4049b75 100644
--- a/src/session/sessiondialog.ui
+++ b/src/session/sessiondialog.ui
@@ -41,13 +41,23 @@
<item>
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
- <widget class="QPushButton" name="open">
+ <widget class="QToolButton" name="open_toolButton">
<property name="text">
<string>Open from File</string>
</property>
</widget>
</item>
<item>
+ <widget class="QToolButton" name="delete_toolButton">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
diff --git a/src/session/sessionform.cpp b/src/session/sessionform.cpp
deleted file mode 100644
index 761cb42..0000000
--- a/src/session/sessionform.cpp
+++ /dev/null
@@ -1,36 +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/gitea/aqua/smolbote
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#include "sessionform.h"
-#include "ui_sessionform.h"
-#include <QStyle>
-
-SessionForm::SessionForm(const QString &path, QWidget *parent)
- : QWidget(parent)
- , ui(new Ui::SessionForm)
-{
- ui->setupUi(this);
- ui->label->setText(path);
- ui->delete_toolButton->setIcon(style()->standardIcon(QStyle::SP_TrashIcon));
- ui->delete_toolButton->hide();
-}
-
-SessionForm::~SessionForm()
-{
- delete ui;
-}
-
-void SessionForm::showDetails()
-{
- ui->delete_toolButton->show();
-}
-
-void SessionForm::hideDetails()
-{
- ui->delete_toolButton->hide();
-}
diff --git a/src/session/sessionform.h b/src/session/sessionform.h
deleted file mode 100644
index 5e5f52e..0000000
--- a/src/session/sessionform.h
+++ /dev/null
@@ -1,37 +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/gitea/aqua/smolbote
- *
- * SPDX-License-Identifier: GPL-3.0
- */
-
-#ifndef SMOLBOTE_SESSIONFORM_H
-#define SMOLBOTE_SESSIONFORM_H
-
-#include <QWidget>
-
-namespace Ui
-{
-class SessionForm;
-}
-
-class SessionForm : public QWidget
-{
- Q_OBJECT
-
- friend class SessionDialog;
-
-public:
- explicit SessionForm(const QString &path, QWidget *parent = nullptr);
- ~SessionForm() override;
-
-public slots:
- void showDetails();
- void hideDetails();
-
-private:
- Ui::SessionForm *ui;
-};
-
-#endif // SMOLBOTE_SESSIONFORM_H
diff --git a/src/session/sessionform.ui b/src/session/sessionform.ui
deleted file mode 100644
index dbf0237..0000000
--- a/src/session/sessionform.ui
+++ /dev/null
@@ -1,41 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>SessionForm</class>
- <widget class="QWidget" name="SessionForm">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>711</width>
- <height>34</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>Form</string>
- </property>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <property name="topMargin">
- <number>0</number>
- </property>
- <property name="bottomMargin">
- <number>0</number>
- </property>
- <item>
- <widget class="QLabel" name="label">
- <property name="text">
- <string>TextLabel</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QToolButton" name="delete_toolButton">
- <property name="text">
- <string>...</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/subwindow/subwindow.cpp b/src/subwindow/subwindow.cpp
index 58acee6..8c2e3e7 100644
--- a/src/subwindow/subwindow.cpp
+++ b/src/subwindow/subwindow.cpp
@@ -10,10 +10,12 @@
#include "browser.h"
#include "configuration.h"
#include "webengine/webprofile.h"
+#include "webengine/webprofilemanager.h"
#include "webengine/webview.h"
#include <QAction>
#include <QCloseEvent>
#include <QHideEvent>
+#include <QJsonArray>
#include <QMenu>
#include <QShortcut>
#include <QShowEvent>
@@ -76,6 +78,35 @@ SubWindow::SubWindow(QWidget *parent, Qt::WindowFlags flags)
});
}
+SubWindow::SubWindow(const Session::SubWindow &data, QWidget *parent, Qt::WindowFlags flags)
+ : SubWindow(parent, flags)
+{
+ const auto *profileManager = WebProfileManager::instance();
+ Q_CHECK_PTR(profileManager);
+
+ auto *profile = profileManager->profile(data.profile);
+ if(profile != nullptr) {
+ setProfile(profile);
+ }
+
+ for(const auto &data : data.tabs) {
+ addTab(data);
+ }
+}
+
+Session::SubWindow SubWindow::serialize() const
+{
+ const auto *profileManager = WebProfileManager::instance();
+ Q_CHECK_PTR(profileManager);
+
+ QVector<Session::WebView> tabs(tabCount());
+ for(int i = 0; i < tabCount(); ++i) {
+ tabs[i] = view(i)->serialize();
+ }
+
+ return { profileManager->id(profile()), tabs };
+}
+
void SubWindow::setProfile(WebProfile *profile)
{
if(profile == nullptr) {
@@ -115,6 +146,12 @@ int SubWindow::addTab(const QUrl &url, WebProfile *profile)
return tabWidget->addTab(view);
}
+int SubWindow::addTab(const Session::WebView &data)
+{
+ auto *view = new WebView(data, this);
+ return tabWidget->addTab(view);
+}
+
void SubWindow::moveTab(int from, int to)
{
tabWidget->tabBar()->moveTab(from, to);
diff --git a/src/subwindow/subwindow.h b/src/subwindow/subwindow.h
index 8343a9b..02f50d0 100644
--- a/src/subwindow/subwindow.h
+++ b/src/subwindow/subwindow.h
@@ -9,6 +9,7 @@
#ifndef SMOLBOTE_SUBWINDOW_H
#define SMOLBOTE_SUBWINDOW_H
+#include "session.hpp"
#include "tabwidget.h"
#include "webengine/webview.h"
#include <QMenu>
@@ -27,7 +28,9 @@ public:
};
explicit SubWindow(QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
+ explicit SubWindow(const Session::SubWindow &data, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags());
~SubWindow() = default;
+ [[nodiscard]] Session::SubWindow serialize() const;
[[nodiscard]] int currentTabIndex() const
{
@@ -66,6 +69,7 @@ signals:
public slots:
int addTab(const QUrl &url = QUrl(), WebProfile *profile = nullptr);
+ int addTab(const Session::WebView &data);
void closeTab(int index)
{
tabWidget->removeTab(index);
diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp
index d42bad5..c64333e 100644
--- a/src/webengine/webview.cpp
+++ b/src/webengine/webview.cpp
@@ -13,15 +13,12 @@
#include "webprofilemanager.h"
#include "webviewcontextmenu.h"
#include <QContextMenuEvent>
+#include <QJsonObject>
#include <QWebEngineHistoryItem>
-WebView::WebView(WebProfile *profile, QWidget *parent)
+WebView::WebView(QWidget *parent)
: QWebEngineView(parent)
{
- Q_CHECK_PTR(profile);
- m_profile = profile;
- setPage(new WebPage(profile, this));
-
m_parentWindow = qobject_cast<SubWindow *>(parent);
// load status and progress
@@ -40,6 +37,34 @@ WebView::WebView(WebProfile *profile, QWidget *parent)
});
}
+WebView::WebView(WebProfile *profile, QWidget *parent)
+ : WebView(parent)
+{
+ Q_CHECK_PTR(profile);
+ m_profile = profile;
+ setPage(new WebPage(profile, this));
+}
+
+WebView::WebView(const Session::WebView &data, QWidget *parent)
+ : WebView(parent)
+{
+ const auto *profileManager = WebProfileManager::instance();
+ Q_CHECK_PTR(profileManager);
+
+ auto *profile = profileManager->profile(data.profile);
+ if(profile != nullptr) {
+ setProfile(profile);
+ }
+
+ if(!data.url.isEmpty())
+ load(QUrl::fromUserInput(data.url));
+ else {
+ QByteArray copy(data.history);
+ QDataStream historyStream(&copy, QIODevice::ReadOnly);
+ historyStream >> *history();
+ }
+}
+
void WebView::setProfile(WebProfile *profile)
{
m_profile = profile;
@@ -48,6 +73,18 @@ void WebView::setProfile(WebProfile *profile)
this->load(url);
}
+Session::WebView WebView::serialize() const
+{
+ const auto *profileManager = WebProfileManager::instance();
+ Q_CHECK_PTR(profileManager);
+
+ QByteArray historyData;
+ QDataStream historyStream(&historyData, QIODevice::WriteOnly);
+ historyStream << *history();
+
+ return { profileManager->id(profile()), QString(), historyData };
+}
+
bool WebView::isLoaded() const
{
return m_loaded;
diff --git a/src/webengine/webview.h b/src/webengine/webview.h
index 0223b78..5748691 100644
--- a/src/webengine/webview.h
+++ b/src/webengine/webview.h
@@ -11,26 +11,32 @@
#include "webpage.h"
#include <QWebEngineView>
+#include <session.hpp>
class WebProfile;
class SubWindow;
class WebViewContextMenu;
-class WebView : public QWebEngineView
+class WebView final : public QWebEngineView
{
friend class WebViewContextMenu;
Q_OBJECT
+ explicit WebView(QWidget *parent = nullptr);
+
public:
explicit WebView(WebProfile *profile = nullptr, QWidget *parent = nullptr);
+ explicit WebView(const Session::WebView &data, QWidget *parent = nullptr);
~WebView() = default;
- WebProfile *profile() const
+ [[nodiscard]] WebProfile *profile() const
{
return m_profile;
}
void setProfile(WebProfile *profile);
+ [[nodiscard]] Session::WebView serialize() const;
+
bool isLoaded() const;
public slots:
diff --git a/subprojects/catch2.wrap b/subprojects/catch2.wrap
new file mode 100644
index 0000000..2e20085
--- /dev/null
+++ b/subprojects/catch2.wrap
@@ -0,0 +1,10 @@
+[wrap-file]
+directory = Catch2-2.11.3
+
+source_url = https://github.com/catchorg/Catch2/archive/v2.11.3.zip
+source_filename = Catch2-2.11.3.zip
+source_hash = c5a0a7510379c6f37f70b329986a335a7b8489d67ac417ce8f4262d0cae4cc5d
+
+patch_url = https://wrapdb.mesonbuild.com/v1/projects/catch2/2.11.3/1/get_zip
+patch_filename = catch2-2.11.3-1-wrap.zip
+patch_hash = 63c09cb68280435040ad304b3dd87ecfe69dbc216608991d0a82569a63119e57