From 4e8b6e48fd5b45ec8c39f2e4661596d3019e3907 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Thu, 23 Apr 2020 21:14:31 +0200 Subject: [PATCH] Browser: Start implementing tabbed browsing! :^) This patch moves most of the Browser UI into a Tab class. The main UI now mainly consists of a GUI::TabWidget that Tab objects are added to. I'm going with the "tabs on top" style here, since I like how it makes it feel like each tab has its own UI controls (which it actually does!) --- Applications/Browser/Makefile | 5 +- Applications/Browser/Tab.cpp | 284 ++++++++++++++++++++++++++++++++++ Applications/Browser/Tab.h | 71 +++++++++ Applications/Browser/main.cpp | 262 ++++--------------------------- 4 files changed, 384 insertions(+), 238 deletions(-) create mode 100644 Applications/Browser/Tab.cpp create mode 100644 Applications/Browser/Tab.h diff --git a/Applications/Browser/Makefile b/Applications/Browser/Makefile index ce65632cd46..dd8d297395d 100755 --- a/Applications/Browser/Makefile +++ b/Applications/Browser/Makefile @@ -1,7 +1,8 @@ OBJS = \ - main.o \ + BookmarksBarWidget.o \ InspectorWidget.o \ - BookmarksBarWidget.o + Tab.o \ + main.o PROGRAM = Browser diff --git a/Applications/Browser/Tab.cpp b/Applications/Browser/Tab.cpp new file mode 100644 index 00000000000..b48cfdc1bd0 --- /dev/null +++ b/Applications/Browser/Tab.cpp @@ -0,0 +1,284 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "Tab.h" +#include "BookmarksBarWidget.h" +#include "History.h" +#include "InspectorWidget.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Browser { + +static const char* home_url = "file:///home/anon/www/welcome.html"; +static const char* bookmarks_filename = "/home/anon/bookmarks.json"; + +Tab::Tab() +{ + auto& widget = *this; + auto& layout = set_layout(); + layout.set_margins({ 1, 1, 1, 1 }); + + bool bookmarksbar_enabled = true; + + auto& toolbar_container = widget.add(); + auto& toolbar = toolbar_container.add(); + m_bookmarks_bar = toolbar_container.add(bookmarks_filename, bookmarksbar_enabled); + m_html_widget = widget.add(); + + m_bookmarks_bar->on_bookmark_click = [this](auto&, auto& url) { + m_html_widget->load(url); + }; + + m_go_back_action = GUI::CommonActions::make_go_back_action([this](auto&) { + m_history.go_back(); + update_actions(); + TemporaryChange change(m_should_push_loads_to_history, false); + m_html_widget->load(m_history.current()); + }); + + m_go_forward_action = GUI::CommonActions::make_go_forward_action([this](auto&) { + m_history.go_forward(); + update_actions(); + TemporaryChange change(m_should_push_loads_to_history, false); + m_html_widget->load(m_history.current()); + }); + + toolbar.add_action(*m_go_back_action); + toolbar.add_action(*m_go_forward_action); + + toolbar.add_action(GUI::CommonActions::make_go_home_action([this](auto&) { + m_html_widget->load(home_url); + })); + + toolbar.add_action(GUI::CommonActions::make_reload_action([this](auto&) { + TemporaryChange change(m_should_push_loads_to_history, false); + m_html_widget->reload(); + })); + + m_location_box = toolbar.add(); + + m_location_box->on_return_pressed = [this] { + m_html_widget->load(m_location_box->text()); + }; + + m_bookmark_button = toolbar.add(); + m_bookmark_button->set_button_style(Gfx::ButtonStyle::CoolBar); + m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-black.png")); + m_bookmark_button->set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); + m_bookmark_button->set_preferred_size(22, 22); + + m_bookmark_button->on_click = [this] { + auto url = m_html_widget->main_frame().document()->url().to_string(); + if (m_bookmarks_bar->contains_bookmark(url)) { + m_bookmarks_bar->remove_bookmark(url); + } else { + m_bookmarks_bar->add_bookmark(url, m_title); + } + update_bookmark_button(url); + }; + + m_html_widget->on_load_start = [this](auto& url) { + m_location_box->set_text(url.to_string()); + if (m_should_push_loads_to_history) + m_history.push(url); + update_actions(); + update_bookmark_button(url.to_string()); + }; + + m_html_widget->on_link_click = [this](auto& url) { + if (url.starts_with("#")) { + m_html_widget->scroll_to_anchor(url.substring_view(1, url.length() - 1)); + } else { + m_html_widget->load(m_html_widget->document()->complete_url(url)); + } + }; + + m_html_widget->on_title_change = [this](auto& title) { + if (title.is_null()) { + m_title = m_html_widget->main_frame().document()->url().to_string(); + } else { + m_title = title; + } + if (on_title_change) + on_title_change(m_title); + }; + + auto focus_location_box_action = GUI::Action::create("Focus location box", { Mod_Ctrl, Key_L }, [this](auto&) { + m_location_box->select_all(); + m_location_box->set_focus(true); + }); + + m_statusbar = widget.add(); + + m_html_widget->on_link_hover = [this](auto& href) { + m_statusbar->set_text(href); + }; + + m_bookmarks_bar->on_bookmark_hover = [this](auto&, auto& url) { + m_statusbar->set_text(url); + }; + + Web::ResourceLoader::the().on_load_counter_change = [this] { + if (Web::ResourceLoader::the().pending_loads() == 0) { + m_statusbar->set_text(""); + return; + } + m_statusbar->set_text(String::format("Loading (%d pending resources...)", Web::ResourceLoader::the().pending_loads())); + }; + + m_menubar = GUI::MenuBar::construct(); + + auto& app_menu = m_menubar->add_menu("Browser"); + app_menu.add_action(GUI::Action::create("Reload", { Mod_None, Key_F5 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"), [this](auto&) { + TemporaryChange change(m_should_push_loads_to_history, false); + m_html_widget->reload(); + })); + app_menu.add_separator(); + app_menu.add_action(GUI::CommonActions::make_quit_action([](auto&) { + GUI::Application::the().quit(); + })); + + auto& inspect_menu = m_menubar->add_menu("Inspect"); + inspect_menu.add_action(GUI::Action::create("View source", { Mod_Ctrl, Key_U }, [this](auto&) { + String filename_to_open; + char tmp_filename[] = "/tmp/view-source.XXXXXX"; + ASSERT(m_html_widget->document()); + if (m_html_widget->document()->url().protocol() == "file") { + filename_to_open = m_html_widget->document()->url().path(); + } else { + int fd = mkstemp(tmp_filename); + ASSERT(fd >= 0); + auto source = m_html_widget->document()->source(); + write(fd, source.characters(), source.length()); + close(fd); + filename_to_open = tmp_filename; + } + if (fork() == 0) { + execl("/bin/TextEditor", "TextEditor", filename_to_open.characters(), nullptr); + ASSERT_NOT_REACHED(); + } + })); + inspect_menu.add_action(GUI::Action::create("Inspect DOM tree", { Mod_None, Key_F12 }, [this](auto&) { + if (!m_dom_inspector_window) { + m_dom_inspector_window = GUI::Window::construct(); + m_dom_inspector_window->set_rect(100, 100, 300, 500); + m_dom_inspector_window->set_title("DOM inspector"); + m_dom_inspector_window->set_main_widget(); + } + auto* inspector_widget = static_cast(m_dom_inspector_window->main_widget()); + inspector_widget->set_document(m_html_widget->document()); + m_dom_inspector_window->show(); + m_dom_inspector_window->move_to_front(); + })); + + auto& debug_menu = m_menubar->add_menu("Debug"); + debug_menu.add_action(GUI::Action::create("Dump DOM tree", [this](auto&) { + Web::dump_tree(*m_html_widget->document()); + })); + debug_menu.add_action(GUI::Action::create("Dump Layout tree", [this](auto&) { + Web::dump_tree(*m_html_widget->document()->layout_node()); + })); + debug_menu.add_action(GUI::Action::create("Dump Style sheets", [this](auto&) { + for (auto& sheet : m_html_widget->document()->stylesheets()) { + dump_sheet(sheet); + } + })); + debug_menu.add_separator(); + auto line_box_borders_action = GUI::Action::create_checkable("Line box borders", [this](auto& action) { + m_html_widget->set_should_show_line_box_borders(action.is_checked()); + m_html_widget->update(); + }); + line_box_borders_action->set_checked(false); + debug_menu.add_action(line_box_borders_action); + + auto& bookmarks_menu = m_menubar->add_menu("Bookmarks"); + auto show_bookmarksbar_action = GUI::Action::create_checkable("Show bookmarks bar", [this](auto& action) { + m_bookmarks_bar->set_visible(action.is_checked()); + m_bookmarks_bar->update(); + }); + show_bookmarksbar_action->set_checked(bookmarksbar_enabled); + bookmarks_menu.add_action(show_bookmarksbar_action); + + auto& help_menu = m_menubar->add_menu("Help"); + help_menu.add_action(GUI::Action::create("About", [this](const GUI::Action&) { + GUI::AboutDialog::show("Browser", Gfx::Bitmap::load_from_file("/res/icons/32x32/filetype-html.png"), window()); + })); +} + +Tab::~Tab() +{ +} + +void Tab::load(const URL& url) +{ + m_html_widget->load(url); +} + +void Tab::update_actions() +{ + m_go_back_action->set_enabled(m_history.can_go_back()); + m_go_forward_action->set_enabled(m_history.can_go_forward()); +} + +void Tab::update_bookmark_button(const String& url) +{ + if (m_bookmarks_bar->contains_bookmark(url)) { + m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-yellow.png")); + } else { + m_bookmark_button->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-contour.png")); + } +} + +void Tab::did_become_active() +{ + GUI::Application::the().set_menubar(m_menubar); +} + +} diff --git a/Applications/Browser/Tab.h b/Applications/Browser/Tab.h new file mode 100644 index 00000000000..c596b5afde4 --- /dev/null +++ b/Applications/Browser/Tab.h @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "History.h" +#include +#include +#include + +class BookmarksBarWidget; + +namespace Browser { + +class Tab final : public GUI::Widget { + C_OBJECT(Tab); + +public: + virtual ~Tab() override; + + void load(const URL&); + + void did_become_active(); + + Function on_title_change; + + const String& title() const { return m_title; } + +private: + Tab(); + + void update_actions(); + void update_bookmark_button(const String& url); + + History m_history; + RefPtr m_html_widget; + RefPtr m_go_back_action; + RefPtr m_go_forward_action; + RefPtr m_location_box; + RefPtr m_bookmarks_bar; + RefPtr m_bookmark_button; + RefPtr m_dom_inspector_window; + RefPtr m_statusbar; + RefPtr m_menubar; + + String m_title; + bool m_should_push_loads_to_history { true }; +}; + +} diff --git a/Applications/Browser/main.cpp b/Applications/Browser/main.cpp index f46dfca84c9..c2ab84b5399 100644 --- a/Applications/Browser/main.cpp +++ b/Applications/Browser/main.cpp @@ -24,42 +24,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "BookmarksBarWidget.h" -#include "History.h" #include "InspectorWidget.h" +#include "Tab.h" #include -#include -#include #include #include -#include -#include -#include -#include -#include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include #include static const char* home_url = "file:///home/anon/www/welcome.html"; -static const char* bookmarks_filename = "/home/anon/bookmarks.json"; - -static String s_title = ""; int main(int argc, char** argv) { @@ -103,227 +80,40 @@ int main(int argc, char** argv) widget.set_layout(); widget.layout()->set_spacing(2); - bool bookmarksbar_enabled = true; + auto& tab_widget = widget.add(); - auto& toolbar_container = widget.add(); - auto& toolbar = toolbar_container.add(); - auto& bookmarksbar = toolbar_container.add(bookmarks_filename, bookmarksbar_enabled); - auto& html_widget = widget.add(); - - bookmarksbar.on_bookmark_click = [&](auto&, auto& url) { - html_widget.load(url); + tab_widget.on_change = [&](auto& active_widget) { + auto& tab = static_cast(active_widget); + window->set_title(String::format("%s - Browser", tab.title().characters())); + tab.did_become_active(); }; - History history; + auto create_new_tab = [&] { + auto& new_tab = tab_widget.add_tab("New tab"); - RefPtr go_back_action; - RefPtr go_forward_action; + new_tab.on_title_change = [&](auto title) { + tab_widget.set_tab_title(new_tab, title); + if (tab_widget.active_widget() == &new_tab) + window->set_title(String::format("%s - Browser", title.characters())); + }; - auto update_actions = [&]() { - go_back_action->set_enabled(history.can_go_back()); - go_forward_action->set_enabled(history.can_go_forward()); - }; + window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); - bool should_push_loads_to_history = true; + window->set_title("Browser"); + window->show(); - go_back_action = GUI::CommonActions::make_go_back_action([&](auto&) { - history.go_back(); - update_actions(); - TemporaryChange change(should_push_loads_to_history, false); - html_widget.load(history.current()); - }); + URL url_to_load = home_url; - go_forward_action = GUI::CommonActions::make_go_forward_action([&](auto&) { - history.go_forward(); - update_actions(); - TemporaryChange change(should_push_loads_to_history, false); - html_widget.load(history.current()); - }); - - toolbar.add_action(*go_back_action); - toolbar.add_action(*go_forward_action); - - toolbar.add_action(GUI::CommonActions::make_go_home_action([&](auto&) { - html_widget.load(home_url); - })); - - toolbar.add_action(GUI::CommonActions::make_reload_action([&](auto&) { - TemporaryChange change(should_push_loads_to_history, false); - html_widget.reload(); - })); - - auto& location_box = toolbar.add(); - - location_box.on_return_pressed = [&] { - html_widget.load(location_box.text()); - }; - - auto& bookmark_button = toolbar.add(); - bookmark_button.set_button_style(Gfx::ButtonStyle::CoolBar); - bookmark_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-black.png")); - bookmark_button.set_size_policy(GUI::SizePolicy::Fixed, GUI::SizePolicy::Fixed); - bookmark_button.set_preferred_size(22, 22); - - auto update_bookmark_button = [&](const String& url) { - if (bookmarksbar.contains_bookmark(url)) { - bookmark_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-yellow.png")); - } else { - bookmark_button.set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/star-contour.png")); + if (app.args().size() >= 1) { + url_to_load = URL::create_with_url_or_path(app.args()[0]); } + + new_tab.load(url_to_load); + + dbg() << "Added new tab " << &new_tab << ", loading " << url_to_load; }; - bookmark_button.on_click = [&] { - auto url = html_widget.main_frame().document()->url().to_string(); - if (bookmarksbar.contains_bookmark(url)) { - bookmarksbar.remove_bookmark(url); - } else { - bookmarksbar.add_bookmark(url, s_title); - } - update_bookmark_button(url); - }; - - html_widget.on_load_start = [&](auto& url) { - location_box.set_text(url.to_string()); - if (should_push_loads_to_history) - history.push(url); - update_actions(); - update_bookmark_button(url.to_string()); - }; - - html_widget.on_link_click = [&](auto& url) { - if (url.starts_with("#")) { - html_widget.scroll_to_anchor(url.substring_view(1, url.length() - 1)); - } else { - html_widget.load(html_widget.document()->complete_url(url)); - } - }; - - html_widget.on_title_change = [&](auto& title) { - if (title.is_null()) { - s_title = html_widget.main_frame().document()->url().to_string(); - } else { - s_title = title; - } - window->set_title(String::format("%s - Browser", s_title.characters())); - }; - - auto focus_location_box_action = GUI::Action::create("Focus location box", { Mod_Ctrl, Key_L }, [&](auto&) { - location_box.select_all(); - location_box.set_focus(true); - }); - - auto& statusbar = widget.add(); - - html_widget.on_link_hover = [&](auto& href) { - statusbar.set_text(href); - }; - - bookmarksbar.on_bookmark_hover = [&](auto&, auto& url) { - statusbar.set_text(url); - }; - - Web::ResourceLoader::the().on_load_counter_change = [&] { - if (Web::ResourceLoader::the().pending_loads() == 0) { - statusbar.set_text(""); - return; - } - statusbar.set_text(String::format("Loading (%d pending resources...)", Web::ResourceLoader::the().pending_loads())); - }; - - auto menubar = GUI::MenuBar::construct(); - - auto& app_menu = menubar->add_menu("Browser"); - app_menu.add_action(GUI::Action::create("Reload", { Mod_None, Key_F5 }, Gfx::Bitmap::load_from_file("/res/icons/16x16/reload.png"), [&](auto&) { - TemporaryChange change(should_push_loads_to_history, false); - html_widget.reload(); - })); - app_menu.add_separator(); - app_menu.add_action(GUI::CommonActions::make_quit_action([&](auto&) { - app.quit(); - })); - - RefPtr dom_inspector_window; - - auto& inspect_menu = menubar->add_menu("Inspect"); - inspect_menu.add_action(GUI::Action::create("View source", { Mod_Ctrl, Key_U }, [&](auto&) { - String filename_to_open; - char tmp_filename[] = "/tmp/view-source.XXXXXX"; - ASSERT(html_widget.document()); - if (html_widget.document()->url().protocol() == "file") { - filename_to_open = html_widget.document()->url().path(); - } else { - int fd = mkstemp(tmp_filename); - ASSERT(fd >= 0); - auto source = html_widget.document()->source(); - write(fd, source.characters(), source.length()); - close(fd); - filename_to_open = tmp_filename; - } - if (fork() == 0) { - execl("/bin/TextEditor", "TextEditor", filename_to_open.characters(), nullptr); - ASSERT_NOT_REACHED(); - } - })); - inspect_menu.add_action(GUI::Action::create("Inspect DOM tree", { Mod_None, Key_F12 }, [&](auto&) { - if (!dom_inspector_window) { - dom_inspector_window = GUI::Window::construct(); - dom_inspector_window->set_rect(100, 100, 300, 500); - dom_inspector_window->set_title("DOM inspector"); - dom_inspector_window->set_main_widget(); - } - auto* inspector_widget = static_cast(dom_inspector_window->main_widget()); - inspector_widget->set_document(html_widget.document()); - dom_inspector_window->show(); - dom_inspector_window->move_to_front(); - })); - - auto& debug_menu = menubar->add_menu("Debug"); - debug_menu.add_action(GUI::Action::create("Dump DOM tree", [&](auto&) { - dump_tree(*html_widget.document()); - })); - debug_menu.add_action(GUI::Action::create("Dump Layout tree", [&](auto&) { - dump_tree(*html_widget.document()->layout_node()); - })); - debug_menu.add_action(GUI::Action::create("Dump Style sheets", [&](auto&) { - for (auto& sheet : html_widget.document()->stylesheets()) { - dump_sheet(sheet); - } - })); - debug_menu.add_separator(); - auto line_box_borders_action = GUI::Action::create_checkable("Line box borders", [&](auto& action) { - html_widget.set_should_show_line_box_borders(action.is_checked()); - html_widget.update(); - }); - line_box_borders_action->set_checked(false); - debug_menu.add_action(line_box_borders_action); - - auto& bookmarks_menu = menubar->add_menu("Bookmarks"); - auto show_bookmarksbar_action = GUI::Action::create_checkable("Show bookmarks bar", [&](auto& action) { - bookmarksbar.set_visible(action.is_checked()); - bookmarksbar.update(); - }); - show_bookmarksbar_action->set_checked(bookmarksbar_enabled); - bookmarks_menu.add_action(show_bookmarksbar_action); - - auto& help_menu = menubar->add_menu("Help"); - help_menu.add_action(GUI::Action::create("About", [&](const GUI::Action&) { - GUI::AboutDialog::show("Browser", Gfx::Bitmap::load_from_file("/res/icons/32x32/filetype-html.png"), window); - })); - - app.set_menubar(move(menubar)); - - window->set_icon(Gfx::Bitmap::load_from_file("/res/icons/16x16/filetype-html.png")); - - window->set_title("Browser"); - window->show(); - - URL url_to_load = home_url; - - if (app.args().size() >= 1) { - url_to_load = URL::create_with_url_or_path(app.args()[0]); - } - - html_widget.load(url_to_load); + create_new_tab(); return app.exec(); }