From 35d8464f8975ab35c1e2f1a076302d9f95bfb22c Mon Sep 17 00:00:00 2001 From: Akshay Nair Date: Tue, 11 Mar 2025 23:02:44 +0530 Subject: Refactor browsermanager to webviewstack --- include/LuaRuntime.hpp | 8 +- include/widgets/BrowserManager.hpp | 44 ------ include/widgets/MainWindow.hpp | 8 +- include/widgets/WebViewStack.hpp | 47 +++++++ spec/BrowserManagerSpec.cpp | 271 ------------------------------------ spec/WebViewStackSpec.cpp | 273 +++++++++++++++++++++++++++++++++++++ src/widgets/BrowserManager.cpp | 151 -------------------- src/widgets/MainWindow.cpp | 29 ++-- src/widgets/WebViewStack.cpp | 136 ++++++++++++++++++ 9 files changed, 479 insertions(+), 488 deletions(-) delete mode 100644 include/widgets/BrowserManager.hpp create mode 100644 include/widgets/WebViewStack.hpp delete mode 100644 spec/BrowserManagerSpec.cpp create mode 100644 spec/WebViewStackSpec.cpp delete mode 100644 src/widgets/BrowserManager.cpp create mode 100644 src/widgets/WebViewStack.cpp diff --git a/include/LuaRuntime.hpp b/include/LuaRuntime.hpp index 83ebf0c..5bfc82c 100644 --- a/include/LuaRuntime.hpp +++ b/include/LuaRuntime.hpp @@ -2,7 +2,7 @@ #include #include -#include "widgets/BrowserManager.hpp" +#include "widgets/WebViewStack.hpp" class LuaRuntime : public QObject { Q_OBJECT @@ -15,14 +15,12 @@ public: void evaluate(QString code); -protected: - LuaRuntime(); - ~LuaRuntime(); - signals: void urlOpened(QString url, OpenType openType); protected: + LuaRuntime(); + ~LuaRuntime(); static int lua_onUrlOpen(lua_State *state); static int lua_onUrlTabOpen(lua_State *state); diff --git a/include/widgets/BrowserManager.hpp b/include/widgets/BrowserManager.hpp deleted file mode 100644 index 8cf265d..0000000 --- a/include/widgets/BrowserManager.hpp +++ /dev/null @@ -1,44 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -enum OpenType { OpenUrl, OpenUrlInTab, OpenUrlInBgTab, OpenUrlInWindow }; - -class BrowserManager : public QWidget { - Q_OBJECT - -public: - inline static const QUrl NewtabURL = QUrl("about:blank"); - -public: - BrowserManager(QWebEngineProfile *profile = new QWebEngineProfile); - - QUrl currentUrl(); - void setCurrentUrl(QUrl url); - - QWebEngineView *createNewWebView(QUrl url = BrowserManager::NewtabURL, - bool focus = false); - - void openUrl(QUrl url = BrowserManager::NewtabURL, - OpenType openType = OpenType::OpenUrl); - - std::vector webViewUrls(); - u_int32_t currentWebViewIndex(); - u_int32_t webViewCount(); - void focusWebView(long index); - void nextWebView(); - void previousWebView(); - - void closeWebView(long index); - void closeCurrentWebView(); - - void onNewWebViewRequest(QWebEngineNewWindowRequest &request); - -private: - QWebEngineProfile *profile; - QStackedLayout *layout; - QList webViewList; -}; diff --git a/include/widgets/MainWindow.hpp b/include/widgets/MainWindow.hpp index 4731fe4..99a3895 100644 --- a/include/widgets/MainWindow.hpp +++ b/include/widgets/MainWindow.hpp @@ -6,8 +6,8 @@ #include #include "LuaRuntime.hpp" -#include "widgets/BrowserManager.hpp" #include "widgets/InputLine.hpp" +#include "widgets/WebViewStack.hpp" class EvaluationType { public: @@ -20,10 +20,12 @@ class MainWindow : public QMainWindow { public: MainWindow(); -protected: +private: void keyPressEvent(QKeyEvent *event) override; void onInputSubmit(QString input); + void evaluateCommand(QString command); + void hideInputLine(); void showInputLine(); void showURLInput(QString url, OpenType openType); @@ -32,7 +34,7 @@ protected: void setEvaluationType(EvaluationType *); private: - BrowserManager *browserManager; + WebViewStack *webViewStack; InputLine *inputLine; LuaRuntime *luaRuntime; QStackedLayout *layout; diff --git a/include/widgets/WebViewStack.hpp b/include/widgets/WebViewStack.hpp new file mode 100644 index 0000000..1d10715 --- /dev/null +++ b/include/widgets/WebViewStack.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +enum OpenType { OpenUrl, OpenUrlInTab, OpenUrlInBgTab, OpenUrlInWindow }; + +class WebViewStack : public QWidget { + Q_OBJECT + +public: + inline static const QUrl NewtabURL = QUrl("about:blank"); + +public: + WebViewStack(QWebEngineProfile *profile = new QWebEngineProfile, + QWidget *parent = nullptr); + + void openUrl(QUrl url = WebViewStack::NewtabURL, + OpenType openType = OpenType::OpenUrl); + + std::vector urls(); + u_int32_t currentWebViewIndex(); + u_int32_t count(); + QUrl currentUrl(); + + void focusWebView(long index); + void next(); + void previous(); + + void close(long index); + void closeCurrent(); + +private: + void setCurrentUrl(QUrl url); + QWebEngineView *createNewWebView(QUrl url = WebViewStack::NewtabURL, + bool focus = false); + +private slots: + void onNewWebViewRequest(QWebEngineNewWindowRequest &request); + +private: + QWebEngineProfile *profile; + QStackedLayout *layout; + QList webViewList; +}; diff --git a/spec/BrowserManagerSpec.cpp b/spec/BrowserManagerSpec.cpp deleted file mode 100644 index efa1ca3..0000000 --- a/spec/BrowserManagerSpec.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include -#include -#include - -#include "testUtils.h" -#include "widgets/BrowserManager.hpp" - -class BrowserManagerSpec : public QObject { - Q_OBJECT - - class FakeNewWindowRequest : public QWebEngineNewWindowRequest { - public: - FakeNewWindowRequest(DestinationType t, const QRect &r, const QUrl &u, - bool b) - : QWebEngineNewWindowRequest(t, r, u, b, nullptr) {} - }; - -private slots: - void testInitialState() { - context("when initialized"); - it("opens a single tab") { - BrowserManager browserManager; - - QCOMPARE(browserManager.webViewCount(), 1); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - browserManager.deleteLater(); - } - } - - void testCreateNewWebView() { - context("when creating a new background webview without a url"); - it("opens new tab url as a background view") { - BrowserManager browserManager; - - browserManager.createNewWebView(); - - QCOMPARE(browserManager.webViewCount(), 2); - std::vector urls = {QUrl(BrowserManager::NewtabURL), - QUrl(BrowserManager::NewtabURL)}; - QCOMPARE(browserManager.webViewUrls(), urls); - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - - context("when creating a new webview with a url and focus is false"); - it("opens the given url in background") { - BrowserManager browserManager; - - browserManager.createNewWebView(QUrl("https://duckduckgo.com"), false); - - QCOMPARE(browserManager.webViewCount(), 2); - std::vector urls = {QUrl(BrowserManager::NewtabURL), - QUrl("https://duckduckgo.com")}; - QCOMPARE(browserManager.webViewUrls(), urls); - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - - context("when creating a new webview with a url and focus is true"); - it("opens the given url as current view") { - BrowserManager browserManager; - - browserManager.createNewWebView(QUrl("https://duckduckgo.com"), true); - - QCOMPARE(browserManager.webViewCount(), 2); - std::vector urls = {QUrl(BrowserManager::NewtabURL), - QUrl("https://duckduckgo.com")}; - QCOMPARE(browserManager.webViewUrls(), urls); - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("https://duckduckgo.com")); - } - } - - void testNextNavigation() { - context("when nextWebView is called"); - context("- and there is only 1 tab"); - it("does nothing") { - BrowserManager browserManager; - - browserManager.nextWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - - context("when nextWebView is called"); - context("- and there are tabs after the current tab"); - it("goes to the next tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("http://a1.com")); - browserManager.createNewWebView(QUrl("http://a2.com")); - - browserManager.nextWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("http://a1.com")); - } - - context("when nextWebView is called"); - context("- and current tab is the last tab"); - it("jumps to the first tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("http://a1.com")); - browserManager.createNewWebView(QUrl("http://a2.com"), true); - QCOMPARE(browserManager.currentWebViewIndex(), 2); - - browserManager.nextWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - } - - void testPreviousNavigation() { - context("when previousWebView is called"); - context("- and there is only 1 tab"); - it("does nothing") { - BrowserManager browserManager; - - browserManager.previousWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - - context("when previousWebView is called"); - context("- and there are tabs before the current tab"); - it("goes to the next tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("http://a1.com")); - browserManager.createNewWebView(QUrl("http://a2.com"), true); - QCOMPARE(browserManager.currentWebViewIndex(), 2); - - browserManager.previousWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("http://a1.com")); - } - - context("when previousWebView is called"); - context("- and current tab is the last tab"); - it("jumps to the last tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("http://a1.com")); - browserManager.createNewWebView(QUrl("http://a2.com")); - - browserManager.previousWebView(); - - QCOMPARE(browserManager.currentWebViewIndex(), 2); - QCOMPARE(browserManager.currentUrl(), QUrl("http://a2.com")); - } - } - - void testCloseWebView() { - context("when closeWebView is called"); - context("- with out of bounds index"); - it("does nothing") { - BrowserManager browserManager; - browserManager.setCurrentUrl(QUrl("https://a.com")); - - browserManager.closeWebView(1); - - QCOMPARE(browserManager.webViewCount(), 1); - QCOMPARE(browserManager.webViewUrls(), - (std::vector{QUrl("https://a.com")})); - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), QUrl("https://a.com")); - } - - context("when closeWebView is called"); - context("- and there is only 1 tab"); - it("closes the tab and opens empty tab in its place") { - BrowserManager browserManager; - browserManager.setCurrentUrl(QUrl("https://a.com")); - QCOMPARE(browserManager.webViewCount(), 1); - - browserManager.closeWebView(0); - - QCOMPARE(browserManager.webViewCount(), 1); - QCOMPARE(browserManager.webViewUrls(), - (std::vector{BrowserManager::NewtabURL})); - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), BrowserManager::NewtabURL); - } - - context("when closeWebView is called"); - context("- with the current tab index"); - context("- and there are some tabs after"); - it("closes the tab and focuses the next tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("https://a1.com"), true); - browserManager.createNewWebView(QUrl("https://a2.com")); - QCOMPARE(browserManager.webViewCount(), 3); - QCOMPARE(browserManager.currentWebViewIndex(), 1); - - browserManager.closeWebView(1); - - QCOMPARE(browserManager.webViewCount(), 2); - QCOMPARE(browserManager.webViewUrls(), - (std::vector{BrowserManager::NewtabURL, - QUrl("https://a2.com")})); - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("https://a2.com")); - } - - context("when closeWebView is called"); - context("- with the current tab index"); - context("- which is the last tab"); - it("closes the tab and focusses previous tab") { - BrowserManager browserManager; - browserManager.createNewWebView(QUrl("https://a1.com")); - browserManager.createNewWebView(QUrl("https://a2.com"), true); - QCOMPARE(browserManager.webViewCount(), 3); - QCOMPARE(browserManager.currentWebViewIndex(), 2); - - browserManager.closeWebView(2); - - QCOMPARE(browserManager.webViewCount(), 2); - QCOMPARE(browserManager.webViewUrls(), - (std::vector{BrowserManager::NewtabURL, - QUrl("https://a1.com")})); - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("https://a1.com")); - } - } - - void testNewWindowRequestSignal() { - context("when webview emits a newWindowRequested signal"); - context("- of type new tab"); - it("opens a new web view and focusses it") { - BrowserManager browserManager; - browserManager.setCurrentUrl(QUrl("https://a.com")); - auto webview = browserManager.findChild(); - - FakeNewWindowRequest windowRequest( - FakeNewWindowRequest::DestinationType::InNewTab, QRect(0, 0, 0, 0), - QUrl("https://new.com"), true); - emit webview->page()->newWindowRequested(windowRequest); - - QCOMPARE(browserManager.webViewCount(), 2); - QCOMPARE( - browserManager.webViewUrls(), - (std::vector{QUrl("https://a.com"), QUrl("https://new.com")})); - QCOMPARE(browserManager.currentWebViewIndex(), 1); - QCOMPARE(browserManager.currentUrl(), QUrl("https://new.com")); - } - - context("when webview emits a newWindowRequested signal"); - context("- of type new background tab"); - it("opens a new web view in the background") { - BrowserManager browserManager; - browserManager.setCurrentUrl(QUrl("https://a.com")); - auto webview = browserManager.findChild(); - - FakeNewWindowRequest windowRequest( - FakeNewWindowRequest::DestinationType::InNewBackgroundTab, - QRect(0, 0, 0, 0), QUrl("https://new.com"), true); - emit webview->page()->newWindowRequested(windowRequest); - - QCOMPARE(browserManager.webViewCount(), 2); - QCOMPARE( - browserManager.webViewUrls(), - (std::vector{QUrl("https://a.com"), QUrl("https://new.com")})); - QCOMPARE(browserManager.currentWebViewIndex(), 0); - QCOMPARE(browserManager.currentUrl(), QUrl("https://a.com")); - } - } -}; - -QTEST_REGISTER(BrowserManagerSpec) -#include "BrowserManagerSpec.moc" diff --git a/spec/WebViewStackSpec.cpp b/spec/WebViewStackSpec.cpp new file mode 100644 index 0000000..5824437 --- /dev/null +++ b/spec/WebViewStackSpec.cpp @@ -0,0 +1,273 @@ +#include +#include +#include + +#include "testUtils.h" +#include "widgets/WebViewStack.hpp" + +class WebViewStackSpec : public QObject { + Q_OBJECT + + class FakeNewWindowRequest : public QWebEngineNewWindowRequest { + public: + FakeNewWindowRequest(DestinationType t, const QRect &r, const QUrl &u, + bool b) + : QWebEngineNewWindowRequest(t, r, u, b, nullptr) {} + }; + +private slots: + void testInitialState() { + context("when initialized"); + it("opens a single tab") { + WebViewStack webViewStack; + + QCOMPARE(webViewStack.count(), 1); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + webViewStack.deleteLater(); + } + } + + void testOpenUrl() { + context("when openUrl is called without an open type"); + it("replaces current tab url with newtab url") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("http://a.com"), OpenType::OpenUrl); + + webViewStack.openUrl(); + + QCOMPARE(webViewStack.count(), 1); + std::vector urls = {QUrl(WebViewStack::NewtabURL)}; + QCOMPARE(webViewStack.urls(), urls); + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + + context("when creating a new webview with a url and focus is false"); + it("opens the given url in background") { + WebViewStack webViewStack; + + webViewStack.openUrl(QUrl("https://duckduckgo.com"), + OpenType::OpenUrlInBgTab); + + QCOMPARE(webViewStack.count(), 2); + std::vector urls = {QUrl(WebViewStack::NewtabURL), + QUrl("https://duckduckgo.com")}; + QCOMPARE(webViewStack.urls(), urls); + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + + context("when creating a new webview with a url and focus is true"); + it("opens the given url as current view") { + WebViewStack webViewStack; + + webViewStack.openUrl(QUrl("https://duckduckgo.com"), + OpenType::OpenUrlInTab); + + QCOMPARE(webViewStack.count(), 2); + std::vector urls = {QUrl(WebViewStack::NewtabURL), + QUrl("https://duckduckgo.com")}; + QCOMPARE(webViewStack.urls(), urls); + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://duckduckgo.com")); + } + } + + void testNextNavigation() { + context("when nextWebView is called"); + context("- and there is only 1 tab"); + it("does nothing") { + WebViewStack webViewStack; + + webViewStack.next(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + + context("when nextWebView is called"); + context("- and there are tabs after the current tab"); + it("goes to the next tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("http://a1.com"), OpenType::OpenUrlInBgTab); + webViewStack.openUrl(QUrl("http://a2.com"), OpenType::OpenUrlInBgTab); + + webViewStack.next(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("http://a1.com")); + } + + context("when nextWebView is called"); + context("- and current tab is the last tab"); + it("jumps to the first tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("http://a1.com"), OpenType::OpenUrlInBgTab); + webViewStack.openUrl(QUrl("http://a2.com"), OpenType::OpenUrlInTab); + QCOMPARE(webViewStack.currentWebViewIndex(), 2); + + webViewStack.next(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + } + + void testPreviousNavigation() { + context("when previousWebView is called"); + context("- and there is only 1 tab"); + it("does nothing") { + WebViewStack webViewStack; + + webViewStack.previous(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + + context("when previousWebView is called"); + context("- and there are tabs before the current tab"); + it("goes to the next tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("http://a1.com"), OpenType::OpenUrlInBgTab); + webViewStack.openUrl(QUrl("http://a2.com"), OpenType::OpenUrlInTab); + QCOMPARE(webViewStack.currentWebViewIndex(), 2); + + webViewStack.previous(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("http://a1.com")); + } + + context("when previousWebView is called"); + context("- and current tab is the last tab"); + it("jumps to the last tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("http://a1.com"), OpenType::OpenUrlInBgTab); + webViewStack.openUrl(QUrl("http://a2.com"), OpenType::OpenUrlInBgTab); + + webViewStack.previous(); + + QCOMPARE(webViewStack.currentWebViewIndex(), 2); + QCOMPARE(webViewStack.currentUrl(), QUrl("http://a2.com")); + } + } + + void testCloseWebView() { + context("when closeWebView is called"); + context("- with out of bounds index"); + it("does nothing") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a.com"), OpenType::OpenUrl); + + webViewStack.close(1); + + QCOMPARE(webViewStack.count(), 1); + QCOMPARE(webViewStack.urls(), (std::vector{QUrl("https://a.com")})); + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://a.com")); + } + + context("when closeWebView is called"); + context("- and there is only 1 tab"); + it("closes the tab and opens empty tab in its place") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a.com"), OpenType::OpenUrl); + QCOMPARE(webViewStack.count(), 1); + + webViewStack.close(0); + + QCOMPARE(webViewStack.count(), 1); + QCOMPARE(webViewStack.urls(), + (std::vector{WebViewStack::NewtabURL})); + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), WebViewStack::NewtabURL); + } + + context("when closeWebView is called"); + context("- with the current tab index"); + context("- and there are some tabs after"); + it("closes the tab and focuses the next tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a1.com"), OpenType::OpenUrlInTab); + webViewStack.openUrl(QUrl("https://a2.com"), OpenType::OpenUrlInBgTab); + QCOMPARE(webViewStack.count(), 3); + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + + webViewStack.close(1); + + QCOMPARE(webViewStack.count(), 2); + QCOMPARE( + webViewStack.urls(), + (std::vector{WebViewStack::NewtabURL, QUrl("https://a2.com")})); + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://a2.com")); + } + + context("when closeWebView is called"); + context("- with the current tab index"); + context("- which is the last tab"); + it("closes the tab and focusses previous tab") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a1.com"), OpenType::OpenUrlInBgTab); + webViewStack.openUrl(QUrl("https://a2.com"), OpenType::OpenUrlInTab); + QCOMPARE(webViewStack.count(), 3); + QCOMPARE(webViewStack.currentWebViewIndex(), 2); + + webViewStack.close(2); + + QCOMPARE(webViewStack.count(), 2); + QCOMPARE( + webViewStack.urls(), + (std::vector{WebViewStack::NewtabURL, QUrl("https://a1.com")})); + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://a1.com")); + } + } + + void testNewWindowRequestSignal() { + context("when webview emits a newWindowRequested signal"); + context("- of type new tab"); + it("opens a new web view and focusses it") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a.com"), OpenType::OpenUrl); + auto webview = webViewStack.findChild(); + QCOMPARE(webViewStack.count(), 1); + + FakeNewWindowRequest windowRequest( + FakeNewWindowRequest::DestinationType::InNewTab, QRect(0, 0, 0, 0), + QUrl("https://new.com"), true); + emit webview->page()->newWindowRequested(windowRequest); + + QCOMPARE(webViewStack.count(), 2); + QCOMPARE( + webViewStack.urls(), + (std::vector{QUrl("https://a.com"), QUrl("https://new.com")})); + QCOMPARE(webViewStack.currentWebViewIndex(), 1); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://new.com")); + } + + context("when webview emits a newWindowRequested signal"); + context("- of type new background tab"); + it("opens a new web view in the background") { + WebViewStack webViewStack; + webViewStack.openUrl(QUrl("https://a.com"), OpenType::OpenUrl); + auto webview = webViewStack.findChild(); + + FakeNewWindowRequest windowRequest( + FakeNewWindowRequest::DestinationType::InNewBackgroundTab, + QRect(0, 0, 0, 0), QUrl("https://new.com"), true); + emit webview->page()->newWindowRequested(windowRequest); + + QCOMPARE(webViewStack.count(), 2); + QCOMPARE( + webViewStack.urls(), + (std::vector{QUrl("https://a.com"), QUrl("https://new.com")})); + QCOMPARE(webViewStack.currentWebViewIndex(), 0); + QCOMPARE(webViewStack.currentUrl(), QUrl("https://a.com")); + } + } +}; + +QTEST_REGISTER(WebViewStackSpec) +#include "WebViewStackSpec.moc" diff --git a/src/widgets/BrowserManager.cpp b/src/widgets/BrowserManager.cpp deleted file mode 100644 index 5ac6479..0000000 --- a/src/widgets/BrowserManager.cpp +++ /dev/null @@ -1,151 +0,0 @@ -#include -#include -#include - -#include "widgets/BrowserManager.hpp" - -BrowserManager::BrowserManager(QWebEngineProfile *profile) : QWidget() { - setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - - layout = new QStackedLayout(this); - layout->setStackingMode(QStackedLayout::StackAll); - layout->setContentsMargins(0, 0, 0, 0); - - this->profile = profile; - - createNewWebView(BrowserManager::NewtabURL, true); -} - -void BrowserManager::openUrl(QUrl url, OpenType openType) { - switch (openType) { - case OpenType::OpenUrl: - setCurrentUrl(url); - break; - case OpenType::OpenUrlInTab: - createNewWebView(url, true); - break; - case OpenType::OpenUrlInBgTab: - createNewWebView(url, false); - break; - case OpenType::OpenUrlInWindow: - // TODO: impl - break; - } -} - -QWebEngineView *BrowserManager::createNewWebView(QUrl url, bool focus) { - auto webview = new QWebEngineView(profile); - webview->setUrl(url); - layout->addWidget(webview); - webViewList.append(webview); - - connect(webview->page(), &QWebEnginePage::newWindowRequested, this, - &BrowserManager::onNewWebViewRequest); - // connect(webview->page(), &QWebEnginePage::windowCloseRequested, this, - // [this, webview]() { - // for (int i = 0; i <= this->webViewList.length(); i++) { - // auto w = this->webViewList.at(0); - // printf("::::: %d\n\n", w == webview); - // if (w == webview) { - // this->closeWebView(i); - // } - // } - // }); - - if (focus) - focusWebView(webViewList.length() - 1); - - return webview; -} - -void BrowserManager::onNewWebViewRequest(QWebEngineNewWindowRequest &request) { - switch (request.destination()) { - case QWebEngineNewWindowRequest::InNewTab: - createNewWebView(request.requestedUrl(), true); - break; - case QWebEngineNewWindowRequest::InNewBackgroundTab: - createNewWebView(request.requestedUrl(), false); - break; - case QWebEngineNewWindowRequest::InNewWindow: - // TODO: Impl - createNewWebView(request.requestedUrl(), true); - break; - case QWebEngineNewWindowRequest::InNewDialog: - // TODO: Impl - createNewWebView(request.requestedUrl(), true); - break; - } -} - -void BrowserManager::nextWebView() { - if (webViewList.isEmpty()) - return; - auto index = currentWebViewIndex() + 1; - auto total = webViewList.length(); - index = index >= total ? index % total : index; - focusWebView(index); -} - -void BrowserManager::previousWebView() { - if (webViewList.isEmpty()) - return; - auto index = currentWebViewIndex() - 1; - auto total = webViewList.length(); - index = index < 0 ? total + index : index; - focusWebView(index); -} - -void BrowserManager::closeCurrentWebView() { - closeWebView(currentWebViewIndex()); -} - -void BrowserManager::closeWebView(long index) { - if (index < 0 || index >= webViewList.length()) - return; - - auto webview = webViewList.at(index); - layout->removeWidget(webview); - webViewList.removeAt(index); - disconnect(webview->page()); - webview->deleteLater(); - - focusWebView(currentWebViewIndex()); - - if (webViewList.isEmpty()) { - createNewWebView(BrowserManager::NewtabURL, true); - } -} - -std::vector BrowserManager::webViewUrls() { - std::vector urls; - for (auto &view : webViewList) - urls.push_back(view->url()); - return urls; -} - -u_int32_t BrowserManager::currentWebViewIndex() { - return layout->currentIndex(); -} -u_int32_t BrowserManager::webViewCount() { return webViewList.length(); } - -void BrowserManager::focusWebView(long index) { - if (webViewList.isEmpty()) - return; - - index = std::max((long)0, std::min(index, (long)webViewList.length() - 1)); - layout->setCurrentIndex(index); -} - -QUrl BrowserManager::currentUrl() { - if (currentWebViewIndex() >= webViewList.length()) - return QUrl("about:newtab"); - - return webViewList.at(currentWebViewIndex())->url(); -} - -void BrowserManager::setCurrentUrl(QUrl url) { - if (currentWebViewIndex() >= webViewList.length()) - return; - - webViewList.at(currentWebViewIndex())->setUrl(url); -} diff --git a/src/widgets/MainWindow.cpp b/src/widgets/MainWindow.cpp index f2825e8..2d936aa 100644 --- a/src/widgets/MainWindow.cpp +++ b/src/widgets/MainWindow.cpp @@ -7,9 +7,9 @@ #include "CommandParser.hpp" #include "completion/CommandsAdapter.hpp" #include "completion/UrlAdapter.hpp" -#include "widgets/BrowserManager.hpp" #include "widgets/InputLine.hpp" #include "widgets/MainWindow.hpp" +#include "widgets/WebViewStack.hpp" MainWindow::MainWindow() { setStyleSheet("background-color: #000; color: #fff;"); @@ -23,8 +23,8 @@ MainWindow::MainWindow() { centralWidget()->setLayout(layout); // Web engine - browserManager = new BrowserManager(new QWebEngineProfile("web-browser")); - layout->addWidget(browserManager); + webViewStack = new WebViewStack(new QWebEngineProfile("web-browser")); + layout->addWidget(webViewStack); // Command input inputLine = new InputLine; @@ -38,7 +38,7 @@ MainWindow::MainWindow() { luaRuntime = LuaRuntime::instance(); connect(luaRuntime, &LuaRuntime::urlOpened, this, [this](QString url, OpenType openType) { - browserManager->openUrl(QUrl(url), openType); + webViewStack->openUrl(QUrl(url), openType); }); } @@ -78,7 +78,7 @@ void MainWindow::onInputSubmit(QString input) { if (dynamic_cast(currentEvaluationType)) { evaluateCommand(input); } else if (auto urlEval = dynamic_cast(currentEvaluationType)) { - browserManager->openUrl(input, urlEval->type()); + webViewStack->openUrl(input, urlEval->type()); } } @@ -94,19 +94,19 @@ void MainWindow::evaluateCommand(QString command) { if (cmd.argsString.trimmed().isEmpty()) showURLInput("", OpenType::OpenUrl); else - browserManager->openUrl(cmd.argsString, OpenType::OpenUrl); + webViewStack->openUrl(cmd.argsString, OpenType::OpenUrl); break; case CommandType::TabOpen: if (cmd.argsString.trimmed().isEmpty()) showURLInput("", OpenType::OpenUrlInTab); else - browserManager->openUrl(cmd.argsString, OpenType::OpenUrlInTab); + webViewStack->openUrl(cmd.argsString, OpenType::OpenUrlInTab); break; case CommandType::TabNext: - browserManager->nextWebView(); + webViewStack->next(); break; case CommandType::TabPrev: - browserManager->previousWebView(); + webViewStack->previous(); break; case CommandType::Noop: break; @@ -117,21 +117,22 @@ void MainWindow::keyPressEvent(QKeyEvent *event) { auto combo = event->keyCombination(); if (combo.key() == Qt::Key_L && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { - showURLInput(browserManager->currentUrl().toString(), OpenType::OpenUrl); + showURLInput(webViewStack->currentUrl().toString(), OpenType::OpenUrl); } else if (combo.key() == Qt::Key_Semicolon && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { showCommandInput(""); } else if (combo.key() == Qt::Key_T && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { - browserManager->createNewWebView(QUrl("https://lite.duckduckgo.com"), true); + webViewStack->openUrl(QUrl("https://lite.duckduckgo.com"), + OpenType::OpenUrlInTab); } else if (combo.key() == Qt::Key_J && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { - browserManager->nextWebView(); + webViewStack->next(); } else if (combo.key() == Qt::Key_K && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { - browserManager->previousWebView(); + webViewStack->previous(); } else if (combo.key() == Qt::Key_W && combo.keyboardModifiers().testFlag(Qt::ControlModifier)) { - browserManager->closeCurrentWebView(); + webViewStack->closeCurrent(); } } diff --git a/src/widgets/WebViewStack.cpp b/src/widgets/WebViewStack.cpp new file mode 100644 index 0000000..8102351 --- /dev/null +++ b/src/widgets/WebViewStack.cpp @@ -0,0 +1,136 @@ +#include +#include +#include + +#include "widgets/WebViewStack.hpp" + +WebViewStack::WebViewStack(QWebEngineProfile *profile, QWidget *parent) + : QWidget(parent), profile(profile) { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + layout = new QStackedLayout(this); + layout->setStackingMode(QStackedLayout::StackAll); + layout->setContentsMargins(0, 0, 0, 0); + + createNewWebView(WebViewStack::NewtabURL, true); +} + +void WebViewStack::openUrl(QUrl url, OpenType openType) { + switch (openType) { + case OpenType::OpenUrl: + setCurrentUrl(url); + break; + case OpenType::OpenUrlInTab: + createNewWebView(url, true); + break; + case OpenType::OpenUrlInBgTab: + createNewWebView(url, false); + break; + case OpenType::OpenUrlInWindow: + createNewWebView(url, true); + break; + } +} + +QWebEngineView *WebViewStack::createNewWebView(QUrl url, bool focus) { + auto webview = new QWebEngineView(profile); + webview->setUrl(url); + layout->addWidget(webview); + webViewList.append(webview); + + connect(webview->page(), &QWebEnginePage::newWindowRequested, this, + &WebViewStack::onNewWebViewRequest); + + if (focus) + focusWebView(webViewList.length() - 1); + + return webview; +} + +void WebViewStack::onNewWebViewRequest(QWebEngineNewWindowRequest &request) { + switch (request.destination()) { + case QWebEngineNewWindowRequest::InNewTab: + createNewWebView(request.requestedUrl(), true); + break; + case QWebEngineNewWindowRequest::InNewBackgroundTab: + createNewWebView(request.requestedUrl(), false); + break; + case QWebEngineNewWindowRequest::InNewWindow: + // TODO: Impl + createNewWebView(request.requestedUrl(), true); + break; + case QWebEngineNewWindowRequest::InNewDialog: + // TODO: Impl + createNewWebView(request.requestedUrl(), true); + break; + } +} + +void WebViewStack::next() { + if (webViewList.isEmpty()) + return; + auto index = currentWebViewIndex() + 1; + auto total = webViewList.length(); + index = index >= total ? index % total : index; + focusWebView(index); +} + +void WebViewStack::previous() { + if (webViewList.isEmpty()) + return; + auto index = currentWebViewIndex() - 1; + auto total = webViewList.length(); + index = index < 0 ? total + index : index; + focusWebView(index); +} + +void WebViewStack::closeCurrent() { close(currentWebViewIndex()); } + +void WebViewStack::close(long index) { + if (index < 0 || index >= webViewList.length()) + return; + + auto webview = webViewList.at(index); + layout->removeWidget(webview); + webViewList.removeAt(index); + disconnect(webview->page()); + webview->deleteLater(); + + focusWebView(currentWebViewIndex()); + + if (webViewList.isEmpty()) { + createNewWebView(WebViewStack::NewtabURL, true); + } +} + +std::vector WebViewStack::urls() { + std::vector urls; + for (auto &view : webViewList) + urls.push_back(view->url()); + return urls; +} + +u_int32_t WebViewStack::currentWebViewIndex() { return layout->currentIndex(); } +u_int32_t WebViewStack::count() { return webViewList.length(); } + +void WebViewStack::focusWebView(long index) { + if (webViewList.isEmpty()) + return; + + index = std::max((long)0, std::min(index, (long)webViewList.length() - 1)); + layout->setCurrentIndex(index); +} + +QUrl WebViewStack::currentUrl() { + if (currentWebViewIndex() >= webViewList.length()) + return QUrl(""); + + return webViewList.at(currentWebViewIndex())->url(); +} + +void WebViewStack::setCurrentUrl(QUrl url) { + if (currentWebViewIndex() >= webViewList.length()) + return; + + webViewList.at(currentWebViewIndex())->setUrl(url); +} -- cgit v1.3.1