/* * 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: git://neueland.iserlohn-fortress.net/smolbote.git * * SPDX-License-Identifier: GPL-3.0 */ #include "mainwindow.h" #include "browser.h" #include "forms/aboutdialog.h" #include "forms/searchform.h" #include "ui_mainwindow.h" #include "widgets/mainwindowmenubar.h" #include #include #include #include #include #include MainWindow::MainWindow(std::shared_ptr config, QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) , tabBar(new MainWindowTabBar(config, this)) , menuBar(new MainWindowMenuBar(config, this)) , m_addressBar(new UrlLineEdit(this)) , m_progressBar(new LoadingBar(this)) { Q_ASSERT(config); m_config = config; // delete this window when it closes setAttribute(Qt::WA_DeleteOnClose, true); // set up UI ui->setupUi(this); QAction *fullscreenAction = new QAction(this); fullscreenAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value("browser.shortcuts.fullscreen").value()))); connect(fullscreenAction, &QAction::triggered, this, &MainWindow::toggleFullscreen); addAction(fullscreenAction); // Dockable widget styling setDockOptions(dockOptions() | AllowTabbedDocks | ForceTabbedDocks); setTabPosition(Qt::LeftDockWidgetArea, QTabWidget::North); setTabPosition(Qt::RightDockWidgetArea, QTabWidget::North); // Add the toolbars // tabToolBar: main menu and tab list ui->mainToolBar->setMovable(m_config->value("browser.ui.tabtoolbarMovable").value()); ui->mainToolBar->addWidget(menuBar); //tabToolBar->addWidget(tabBar); // navigationToolBar: address bar ui->navigationToolBar->setMovable(m_config->value("browser.ui.navtoolbarMovable").value()); insertToolBarBreak(ui->navigationToolBar); // page actions m_backButton = new NavigationButton(NavigationButton::BackButton, this); m_forwardButton = new NavigationButton(NavigationButton::ForwardButton, this); m_reloadButton = new NavigationButton(NavigationButton::ReloadButton, this); QToolButton *homepageButton = new QToolButton(this); homepageButton->setIcon(style()->standardIcon(QStyle::SP_DirHomeIcon)); connect(homepageButton, &QToolButton::clicked, this, [&]() { tabBar->currentView()->load(m_profile->homepage()); }); ui->navigationToolBar->addWidget(m_backButton); ui->navigationToolBar->addWidget(m_forwardButton); ui->navigationToolBar->addWidget(m_reloadButton); ui->navigationToolBar->addWidget(homepageButton); ui->navigationToolBar->addWidget(m_addressBar); // connect signals connect(m_addressBar, &UrlLineEdit::addressEntered, this, [&](const QUrl &url) { tabBar->currentView()->load(url); }); connect(m_addressBar, &UrlLineEdit::searchTermEntered, this, [&](const QString &term) { QString t = term; t.replace(' ', '+'); QString url = QString::fromStdString(m_config->value("profile.search").value()); url.replace("$term", t); tabBar->currentView()->load(QUrl::fromUserInput(url)); }); connect(tabBar, &MainWindowTabBar::currentTabChanged, this, &MainWindow::handleTabChanged); //connect(tabBar, SIGNAL(currentTabChanged(WebView *)), this, SLOT(handleTabChanged(WebView *))); // loading bar ui->statusBar->addPermanentWidget(m_progressBar); // search box m_searchBox = new SearchForm(this); ui->statusBar->addWidget(m_searchBox); m_searchBox->setVisible(false); // shortcuts QAction *focusAddressAction = new QAction(this); focusAddressAction->setShortcut(QKeySequence(QString::fromStdString(m_config->value("browser.shortcuts.focusAddress").value()))); //focusAddressAction->setShortcut(QKeySequence::fromString(browser->settings()->value("window.shortcuts.focusAddress").toString())); //connect(focusAddressAction, SIGNAL(triggered(bool)), this, SLOT(focusAddress())); connect(focusAddressAction, &QAction::triggered, this, [this]() { m_addressBar->setFocus(); m_addressBar->selectAll(); }); addAction(focusAddressAction); resize(m_config->value("browser.window.width").value(), m_config->value("browser.window.height").value()); if(m_config->value("browser.window.maximized").value()) { showMaximized(); } } MainWindow::~MainWindow() { // Release all dock widgets before deleting so we don't accidentally delete them // Also fixes that annoying crash when closing QList allDockWidgets = findChildren(); for(QDockWidget *w : allDockWidgets) { if(w->widget()) { w->widget()->setParent(nullptr); } } delete ui; } void MainWindow::addTabbedDock(Qt::DockWidgetArea area, QWidget *widget) { // make a list of widgets in the area we want // this way we can append the new dock widget to the last one QVector areaDockWidgets; for(QDockWidget *w : findChildren()) { // check if widget is already shown if(w->widget() == widget) { // in this case, close the dock and return w->close(); return; } if(dockWidgetArea(w) == area) { areaDockWidgets.append(w); } } // create a dock widget QDockWidget *dock = new QDockWidget(widget->windowTitle(), this); dock->setAttribute(Qt::WA_DeleteOnClose, true); // when the dock widget becomes invisble, release the docked widget // setting the widget makes the dock its parent; setting parent back to nullptr // makes the dock not show the widget any more connect(dock, &QDockWidget::visibilityChanged, [dock](bool visible) { if(!visible && dock->widget()) { dock->widget()->setParent(nullptr); } }); dock->setWidget(widget); // the same widget may be shown by docks in other windows // in that case, they grab ownership and the current dock won't be showing anything // so the current widget needs to be closed auto *w = dynamic_cast(widget); w->closeOthers(); if(w) { connect(w, &BookmarksWidget::closeOthersSignal, dock, [dock]() { dock->close(); }); } if(areaDockWidgets.empty()) { // no other widgets addDockWidget(area, dock); } else { // there are other widgets, so put it after the last one tabifyDockWidget(areaDockWidgets.last(), dock); } } void MainWindow::newTab(const QUrl &url) { if(!m_tabBarAdded) { m_tabBarAdded = true; ui->mainToolBar->addWidget(tabBar); } tabBar->addTab(createWebView(url, m_profile.get(), this)); } MainWindow *MainWindow::newWindow(const QUrl &url) { Browser *instance = static_cast(qApp->instance()); return instance->createSession(m_profile->storageName(), true, QStringList(url.toString())); } void MainWindow::closeEvent(QCloseEvent *event) { if(tabBar->count() > 1) { int ret = QMessageBox::warning(this, tr("Close window?"), tr("Close multiple tabs?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No); if(ret == QMessageBox::No) { event->ignore(); return; } } QMainWindow::closeEvent(event); } void MainWindow::about() { AboutDialog *dlg = new AboutDialog(this); dlg->exec(); } void MainWindow::showSettingsDialog() { SettingsDialog *dlg = new SettingsDialog(m_config, this); dlg->exec(); } void MainWindow::setProfile(std::shared_ptr profile) { Q_ASSERT(profile); m_profile = profile; tabBar->setProfile(profile.get()); menuBar->setProfileName(profile->name()); } WebEngineProfile *MainWindow::profile() { Q_ASSERT(m_profile); return m_profile.get(); } void MainWindow::setBookmarksWidget(std::shared_ptr &widget) { Q_ASSERT(widget); m_bookmarksWidget = widget; m_addressBar->setCompleterModel(m_bookmarksWidget->model()); connect(menuBar->bookmarksAction(), &QAction::triggered, this, [this]() { addTabbedDock(Qt::RightDockWidgetArea, m_bookmarksWidget.get()); }); connect(m_bookmarksWidget.get(), &BookmarksWidget::openUrl, this, [this](const QUrl &url) { if(isActiveWindow()) { newTab(url); } }); } void MainWindow::setDownloadsWidget(std::shared_ptr &widget) { Q_ASSERT(widget); m_downloadsWidget = widget; } void MainWindow::toggleFullscreen() { if(isFullScreen()) { setWindowState(Qt::WindowMaximized | Qt::WindowActive); } else { setWindowState(Qt::WindowFullScreen | Qt::WindowActive); } } void MainWindow::handleTabChanged(WebView *view) { Q_CHECK_PTR(view); m_currentView = view; // centralWidget can be a nullptr if(centralWidget()) { // clear the parent of the central widget so it doesn't get deleted centralWidget()->setParent(nullptr); // disconnect signals disconnect(centralWidget()); } // set new central widget setCentralWidget(view); // connect signals m_backButton->setView(view); m_forwardButton->setView(view); m_reloadButton->setView(view); connect(view, &WebView::urlChanged, m_addressBar, &UrlLineEdit::setUrl); m_addressBar->setUrl(view->url()); m_addressBar->pageAction()->setMenu(view->pageMenu()); connect(view, &WebView::titleChanged, this, &MainWindow::handleTitleUpdated); m_progressBar->connectWebView(view); // update UI this->handleTitleUpdated(view->title()); centralWidget()->setFocus(); } void MainWindow::handleTitleUpdated(const QString &title) { QString t = QString::fromStdString(m_config->value("browser.window.title").value()); t.replace("title", title); t.replace("profile", m_profile->name()); setWindowTitle(t); //setWindowTitle(browser->settings()->value("window.title").toString().replace("title", title).replace("profile", tabBar->profile()->name())); } void MainWindow::addPlugins(QVector &plugins) { for(PluginInterface *plugin : plugins) { ProfileInterface *profilePlugin = dynamic_cast(plugin); QWidget *w = profilePlugin->createWidget(m_profile.get(), this); QAction *profileAction = new QAction(this); profileAction->setText("Profile Action"); ui->navigationToolBar->addAction(profileAction); connect(profileAction, &QAction::triggered, this, [this, w]() { w->setVisible(!w->isVisible()); if(w->isVisible()) { w->adjustSize(); QPoint pos = ui->navigationToolBar->pos(); pos.setX(pos.x() + ui->navigationToolBar->width() - w->width()); pos.setY(pos.y() + ui->navigationToolBar->height()); w->move(mapToGlobal(pos)); w->show(); } }); } }