diff options
Diffstat (limited to 'src/subwindow')
-rw-r--r-- | src/subwindow/subwindow.cpp | 176 | ||||
-rw-r--r-- | src/subwindow/subwindow.h | 51 | ||||
-rw-r--r-- | src/subwindow/tabwidget.cpp | 123 | ||||
-rw-r--r-- | src/subwindow/tabwidget.h | 37 |
4 files changed, 387 insertions, 0 deletions
diff --git a/src/subwindow/subwindow.cpp b/src/subwindow/subwindow.cpp new file mode 100644 index 0000000..580bf68 --- /dev/null +++ b/src/subwindow/subwindow.cpp @@ -0,0 +1,176 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "subwindow.h" +#include "browser.h" +#include "webengine/webview.h" +#include "tabwidget.h" +#include <QAction> +#include <QJsonArray> +#include <QJsonDocument> +#include <QJsonObject> +#include <QMenu> +#include <QShortcut> +#include <QStyle> +#include <QToolButton> +#include <webprofile.h> +#include "profilemanager.h" +#include <configuration.h> + +SubWindow::SubWindow(const std::unique_ptr<Configuration> &config, QWidget *parent, Qt::WindowFlags flags) + : QMdiSubWindow(parent, flags) + , tabWidget(new TabWidget(this)) +{ + // delete this window when it closes + setAttribute(Qt::WA_DeleteOnClose, true); + + resize(800, 600); + setWidget(tabWidget); + + m_profile = WebProfile::defaultProfile(); + + // system menu + { + QMenu *menu = systemMenu(); + auto *firstAction = menu->actions().at(0); + + auto *profileName_action = new QAction(tr("Profile: %1").arg(m_profile->name()), menu); + profileName_action->setEnabled(false); + menu->insertAction(firstAction, profileName_action); + + auto *loadProfile_menu = new QMenu(tr("Load profile"), menu); + menu->insertMenu(firstAction, loadProfile_menu); + + Browser *browser = qobject_cast<Browser *>(qApp); + Q_CHECK_PTR(browser); + + ProfileIterator it(ProfileManager::profileList()); + while(it.hasNext()) { + it.next(); + auto *profile = it.value(); + auto *loadAction = loadProfile_menu->addAction(profile->name()); + + connect(loadAction, &QAction::triggered, this, [=]() { + this->setProfile(profile); + profileName_action->setText(tr("Profile: %1").arg(profile->name())); + }); + } + + menu->insertSeparator(firstAction); + } + + // new tab button + auto *newTab_button = new QToolButton(this); + newTab_button->setIcon(style()->standardIcon(QStyle::SP_FileIcon)); + newTab_button->setToolTip(tr("Add tab")); + newTab_button->setShortcut(QKeySequence(config->value<QString>("window.shortcuts.new").value())); + connect(newTab_button, &QToolButton::clicked, this, [=]() { + auto index = addTab(WebProfile::defaultProfile()->newtab()); + tabWidget->setCurrentIndex(index); + }); + tabWidget->setCornerWidget(newTab_button, Qt::TopRightCorner); + + // general actions + auto *closeTab_shortcut = new QShortcut(QKeySequence(config->value<QString>("window.shortcuts.close").value()), this); + connect(closeTab_shortcut, &QShortcut::activated, this, [=]() { + tabWidget->deleteTab(tabWidget->currentIndex()); + }); + + auto *leftTab_shortcut = new QShortcut(QKeySequence(config->value<QString>("window.shortcuts.left").value()), this); + connect(leftTab_shortcut, &QShortcut::activated, this, [=]() { + tabWidget->setCurrentIndex(qMax(0, tabWidget->currentIndex() - 1)); + }); + + auto *rightTab_shortcut = new QShortcut(QKeySequence(config->value<QString>("window.shortcuts.right").value()), this); + connect(rightTab_shortcut, &QShortcut::activated, this, [=]() { + tabWidget->setCurrentIndex(qMin(tabWidget->currentIndex() + 1, tabWidget->count() - 1)); + }); + + auto *fullScreen_shortcut = new QShortcut(QKeySequence(config->value<QString>("window.shortcuts.fullscreen").value()), this); + connect(fullScreen_shortcut, &QShortcut::activated, this, [=]() { + auto *w = this->window(); + if(w->isFullScreen()) + w->showNormal(); + else + w->showFullScreen(); + }); + + connect(tabWidget, &TabWidget::currentChanged, [this](int index) { + if(index < 0) { + // last tab has been closed + close(); + } else { + auto *view = dynamic_cast<WebView *>(tabWidget->widget(index)); + Q_CHECK_PTR(view); + + disconnect(titleConnection); + disconnect(linkHoveredConnection); + + titleConnection = connect(view, &WebView::titleChanged, this, [this](const QString &title) { + auto *v = qobject_cast<WebView *>(sender()); + this->setWindowTitle(QString("%1 :%2").arg(title, v->profile()->name())); + }); + setWindowTitle(QString("%1 :%2").arg(view->title(), view->profile()->name())); + + linkHoveredConnection = connect(view->page(), &WebPage::linkHovered, this, [this](const QString &url) { + if(!url.isEmpty()) + emit showStatusMessage(url, 3000); + }); + + emit currentViewChanged(view); + } + }); +} + +SubWindow::~SubWindow() +{ + delete tabWidget; +} + +WebView *SubWindow::currentView() +{ + return qobject_cast<WebView *>(tabWidget->currentWidget()); +} + +WebView *SubWindow::view(int index) const +{ + return qobject_cast<WebView *>(tabWidget->widget(index)); +} + +int SubWindow::tabCount() const +{ + return tabWidget->count(); +} + +void SubWindow::setProfile(WebProfile *profile) +{ + Q_CHECK_PTR(profile); + this->m_profile = profile; + for(int i = 0; i < tabWidget->count(); ++i) { + auto *view = qobject_cast<WebView *>(tabWidget->widget(i)); + view->setProfile(profile); + } +} + +WebProfile *SubWindow::profile() const +{ + return m_profile; +} + +int SubWindow::addTab(const QUrl &url, WebProfile *profile) +{ + auto *view = new WebView((profile == nullptr) ? m_profile : profile, this); + if(!url.isEmpty()) + view->load(url); + return tabWidget->addTab(view); +} + +void SubWindow::setCurrentTab(int index) +{ + tabWidget->setCurrentIndex(index); +} diff --git a/src/subwindow/subwindow.h b/src/subwindow/subwindow.h new file mode 100644 index 0000000..b7c4aee --- /dev/null +++ b/src/subwindow/subwindow.h @@ -0,0 +1,51 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_SUBWINDOW_H +#define SMOLBOTE_SUBWINDOW_H + +#include <QMdiSubWindow> +#include <QUrl> +#include <memory> + +class TabWidget; +class WebView; +class WebProfile; +class Configuration; +class SubWindow : public QMdiSubWindow +{ + Q_OBJECT + +public: + explicit SubWindow(const std::unique_ptr<Configuration> &config, QWidget *parent = nullptr, Qt::WindowFlags flags = Qt::WindowFlags()); + ~SubWindow() override; + + WebView *currentView(); + WebView *view(int index) const; + int tabCount() const; + + void setProfile(WebProfile *profile); + WebProfile *profile() const; + +signals: + void currentViewChanged(WebView *view); + void showStatusMessage(const QString &message, int timeout = 0); + +public slots: + int addTab(const QUrl &url = QUrl(), WebProfile *profile = nullptr); + void setCurrentTab(int index); + +private: + WebProfile *m_profile; + TabWidget *tabWidget; + + QMetaObject::Connection titleConnection; + QMetaObject::Connection linkHoveredConnection; +}; + +#endif // SMOLBOTE_SUBWINDOW_H diff --git a/src/subwindow/tabwidget.cpp b/src/subwindow/tabwidget.cpp new file mode 100644 index 0000000..1ddfc95 --- /dev/null +++ b/src/subwindow/tabwidget.cpp @@ -0,0 +1,123 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#include "tabwidget.h" +#include "browser.h" +#include "webengine/webview.h" +#include <QAction> +#include <QContextMenuEvent> +#include <QMenu> +#include <QTabBar> +#include <webprofile.h> + +TabWidget::TabWidget(QWidget *parent) + : QTabWidget(parent) +{ + setStyleSheet("QTabBar::tab { width: 200px; }"); + + setTabsClosable(true); + //setTabBarAutoHide(true); + setElideMode(Qt::ElideRight); + setMovable(true); + + connect(this, &TabWidget::tabCloseRequested, this, &TabWidget::deleteTab); + + // when changing tabs, give focus to the widget + // otherwise when closing tabs, the tabwidget will retain focus + connect(this, &TabWidget::currentChanged, this, [this](int index) { + if(widget(index)) + widget(index)->setFocus(); + }); + + // context menu + tabContextMenu = new QMenu(this); + auto *closeTab = tabContextMenu->addAction(tr("Close Tab")); + connect(closeTab, &QAction::triggered, this, [this]() { + deleteTab(this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos()))); + }); + + auto *closeTabsLeft = tabContextMenu->addAction(tr("Close Tabs left")); + connect(closeTabsLeft, &QAction::triggered, this, [this]() { + int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos())); + for(int i = idx - 1; i >= 0; --i) { + deleteTab(i); + } + }); + + auto *closeTabsRight = tabContextMenu->addAction(tr("Close Tabs right")); + connect(closeTabsRight, &QAction::triggered, this, [this]() { + int idx = this->tabBar()->tabAt(mapFromGlobal(tabContextMenu->pos())); + for(int i = count() - 1; i > idx; --i) { + deleteTab(i); + } + }); +} + +TabWidget::~TabWidget() +{ + for(int i = count() - 1; i >= 0; i--) { + delete widget(i); + } +} + +int TabWidget::addTab(WebView *view) +{ + Q_ASSERT_X(view != nullptr, "TabWidget::addTab", "Tried to add null view"); + + int idx = QTabWidget::addTab(view, view->title()); + connect(view, &WebView::titleChanged, [this, view](const QString &title) { + int idx = this->indexOf(view); + Q_ASSERT(idx != -1); + + this->setTabText(idx, title); + }); + connect(view, &WebView::iconChanged, [this, view](const QIcon &icon) { + int idx = this->indexOf(view); + Q_ASSERT(idx != -1); + + this->setTabIcon(idx, icon); + }); + + return idx; +} + +void TabWidget::deleteTab(int index) +{ + // deleting the widget automatically removes the tab? + auto *w = widget(index); + disconnect(w); + delete w; + + if(count() == 0) + parentWidget()->close(); +} + +void TabWidget::contextMenuEvent(QContextMenuEvent *event) +{ + // check if the context menu was called on a tab + int tabIndex = tabBar()->tabAt(event->pos()); + if(tabIndex < 0) { + return; + } + + tabContextMenu->exec(event->globalPos()); +} + +void TabWidget::mousePressEvent(QMouseEvent *event) +{ + if(event->button() == Qt::MiddleButton) { + int index = tabBar()->tabAt(event->pos()); + if(index >= 0) { + deleteTab(index); + } + event->accept(); + return; + } + + QTabWidget::mousePressEvent(event); +} diff --git a/src/subwindow/tabwidget.h b/src/subwindow/tabwidget.h new file mode 100644 index 0000000..793e041 --- /dev/null +++ b/src/subwindow/tabwidget.h @@ -0,0 +1,37 @@ +/* + * This file is part of smolbote. It's copyrighted by the contributors recorded + * in the version control history of the file, available from its original + * location: https://neueland.iserlohn-fortress.net/smolbote.hg + * + * SPDX-License-Identifier: GPL-3.0 + */ + +#ifndef SMOLBOTE_TABWIDGET_H +#define SMOLBOTE_TABWIDGET_H + +#include <QTabWidget> + +class QAction; +class QMenu; +class WebView; +class TabWidget : public QTabWidget +{ + Q_OBJECT + +public: + explicit TabWidget(QWidget *parent = nullptr); + ~TabWidget() override; + +public slots: + int addTab(WebView *view); + void deleteTab(int index); + +protected: + void contextMenuEvent(QContextMenuEvent *event) override; + void mousePressEvent(QMouseEvent *event) override; + +private: + QMenu *tabContextMenu; +}; + +#endif // SMOLBOTE_TABWIDGET_H |