diff options
-rw-r--r-- | meson.build | 4 | ||||
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/wallet/wallet.cpp | 41 | ||||
-rw-r--r-- | src/webengine/webview.cpp | 219 | ||||
-rw-r--r-- | src/webengine/webview.h | 4 | ||||
-rw-r--r-- | src/webengine/webviewcontextmenu.cpp | 227 | ||||
-rw-r--r-- | src/webengine/webviewcontextmenu.h | 21 |
7 files changed, 272 insertions, 245 deletions
diff --git a/meson.build b/meson.build index 49f3990..c033cac 100644 --- a/meson.build +++ b/meson.build @@ -39,10 +39,6 @@ add_project_arguments(cxx.get_supported_arguments([ '-Wimplicit-fallthrough', ]), language: 'cpp') -add_project_link_arguments(cxx.get_supported_link_arguments([ - '-fuse-ld=gold' -]), language: 'cpp') - mod_qt5 = import('qt5') dep_qt5 = dependency('qt5', modules: ['Core', 'Network', 'Widgets', 'WebEngineWidgets', 'Concurrent'], diff --git a/src/meson.build b/src/meson.build index 956c41b..6d6a1aa 100644 --- a/src/meson.build +++ b/src/meson.build @@ -45,6 +45,7 @@ poi_sourceset.add(files( 'webengine/urlinterceptor.cpp', 'webengine/webpage.cpp', 'webengine/webview.cpp', + 'webengine/webviewcontextmenu.cpp', 'webengine/webprofile.cpp', 'webengine/webprofilemanager.cpp', diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index c795cf2..5023ea4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -8,37 +8,28 @@ #include "wallet.h" #include <QWebEngineView> - -#ifdef PLASMA -#include <kwallet.h> -#endif +#include <QWebEngineScript> void Wallet::autocompleteForm(QWebEngineView *view) { -#ifdef PLASMA - const auto findFormFunction = QLatin1Literal("index = undefined; for(var i = 0; i < document.forms.length; ++i) { if(document.forms[i].autocomplete) { index = i } }; index"); - view->page()->runJavaScript(findFormFunction, [view](const QVariant &v) { + const auto findFormFunction = QLatin1String("index = undefined; for(var i = 0; i < document.forms.length; ++i) { if(document.forms[i].autocomplete) { index = i } }; index"); + + view->page()->runJavaScript(findFormFunction, QWebEngineScript::ApplicationWorld, [view](const QVariant &v) { if(!v.isNull()) { + const QString username = "test-username"; + const QString password = "test-password"; + auto autofillFunction = QString("inputs = document.forms[%1].getElementsByTagName('input');" "for(var i = 0; i < inputs.length; ++i) {" - " if(inputs[i].type == 'email') { inputs[i].value='%2' }" - " if(inputs[i].type == 'password') { inputs[i].value='%3' }" - "}"); - - auto *wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), view->window()->winId()); - if(wallet) { - wallet->setFolder("smolbote"); - QMap<QString, QString> map; - wallet->readMap(view->url().host(), map); - qDebug() << map; - - const auto username = map.firstKey(); - QString password; - wallet->readPassword(map.value(username), password); - view->page()->runJavaScript(autofillFunction.arg(v.toString(), username, password)); - } - delete wallet; + " if(inputs[i].type == 'username') { inputs[i].value='username' }" + " else if(inputs[i].type == 'email') { inputs[i].value='%2' }" + " else if(inputs[i].type == 'password') { inputs[i].value='%3' }" + " else { inputs[i].value=inputs[i].type }" + "}") + .arg(v.toString(), username, password); + // TODO + // for page->url() get list of pairs type = value + view->page()->runJavaScript(autofillFunction, QWebEngineScript::ApplicationWorld); } }); -#endif } diff --git a/src/webengine/webview.cpp b/src/webengine/webview.cpp index 5b2b6fd..d42bad5 100644 --- a/src/webengine/webview.cpp +++ b/src/webengine/webview.cpp @@ -8,35 +8,12 @@ #include "webview.h" #include "subwindow/subwindow.h" -#include "wallet/wallet.h" #include "webpage.h" #include "webprofile.h" #include "webprofilemanager.h" +#include "webviewcontextmenu.h" #include <QContextMenuEvent> -#include <QDialog> -#include <QMenu> -#include <QSlider> -#include <QStatusBar> -#include <QStyle> -#include <QToolButton> -#include <QVBoxLayout> -#include <QWebEngineContextMenuData> #include <QWebEngineHistoryItem> -#include <QWidgetAction> - -inline QAction *historyAction(QWebEngineView *view, const QWebEngineHistoryItem &item) -{ - QAction *action = new QAction(view); - if(item.title().isEmpty()) - action->setText(item.url().toString()); - else - action->setText(QObject::tr("%1 (%2)").arg(item.title(), item.url().toString())); - - QObject::connect(action, &QAction::triggered, view, [view, item]() { - view->history()->goToItem(item); - }); - return action; -} WebView::WebView(WebProfile *profile, QWidget *parent) : QWebEngineView(parent) @@ -113,197 +90,7 @@ WebView *WebView::createWindow(QWebEnginePage::WebWindowType type) void WebView::contextMenuEvent(QContextMenuEvent *event) { - QMenu *menu = new QMenu(this); - const auto ctxdata = page()->contextMenuData(); - - // back, forward, reload, mute buttons, added to all variants of the context menu - { - auto *navButtons = new QWidgetAction(this); - - auto *buttons = new QWidget(this); - auto *buttonsLayout = new QHBoxLayout(); - buttonsLayout->setContentsMargins(8, 0, 8, 0); - buttonsLayout->setSpacing(2); - - auto *backButton = new QToolButton(this); - backButton->setEnabled(history()->canGoBack()); - backButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack)); - connect(backButton, &QToolButton::clicked, this, [this, menu]() { - this->back(); - menu->close(); - }); - buttonsLayout->addWidget(backButton); - - auto *forwardButton = new QToolButton(this); - forwardButton->setEnabled(history()->canGoForward()); - forwardButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward)); - connect(forwardButton, &QToolButton::clicked, this, [this, menu]() { - this->forward(); - menu->close(); - }); - buttonsLayout->addWidget(forwardButton); - - auto *refreshButton = new QToolButton(this); - refreshButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload)); - connect(refreshButton, &QToolButton::clicked, this, [this, menu]() { - this->reload(); - menu->close(); - }); - buttonsLayout->addWidget(refreshButton); - - buttonsLayout->addStretch(); - - auto *muteButton = new QToolButton(this); - muteButton->setCheckable(true); - muteButton->setChecked(this->page()->isAudioMuted()); - QIcon muteIcon; - muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolume), QIcon::Normal, QIcon::Off); - muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolumeMuted), QIcon::Normal, QIcon::On); - muteButton->setIcon(muteIcon); - connect(muteButton, &QToolButton::clicked, this, [this, menu](bool checked) { - this->page()->setAudioMuted(checked); - menu->close(); - }); - buttonsLayout->addWidget(muteButton); - - buttons->setLayout(buttonsLayout); - navButtons->setDefaultWidget(buttons); - - menu->addAction(navButtons); - menu->addSeparator(); - } - - if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeNone) { - auto *backMenu = new QMenu(tr("Back"), this); - if(!history()->canGoBack()) { - backMenu->setEnabled(false); - } else { - connect(backMenu, &QMenu::aboutToShow, this, [this, backMenu]() { - backMenu->clear(); - const auto backItems = history()->backItems(10); - for(const QWebEngineHistoryItem &item : backItems) { - backMenu->addAction(historyAction(this, item)); - } - }); - } - menu->addMenu(backMenu); - - auto *forwardMenu = new QMenu(tr("Forward"), this); - if(!history()->canGoForward()) { - forwardMenu->setEnabled(false); - } else { - connect(forwardMenu, &QMenu::aboutToShow, this, [this, forwardMenu]() { - forwardMenu->clear(); - const auto forwardItems = history()->forwardItems(10); - for(const QWebEngineHistoryItem &item : forwardItems) { - forwardMenu->addAction(historyAction(this, item)); - } - }); - } - menu->addMenu(forwardMenu); - - connect(menu->addAction(tr("Reload")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::Reload); - }); - connect(menu->addAction(tr("Reload and bypass Cache")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::ReloadAndBypassCache); - }); - - menu->addSeparator(); - - connect(menu->addAction(tr("Select All")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::SelectAll); - }); - connect(menu->addAction(tr("Clear Selection")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::Unselect); - }); - connect(menu->addAction(tr("Copy to clipboard")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::Copy); - }); - - } else if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeImage) { - connect(menu->addAction(tr("Copy image to clipboard")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::CopyImageToClipboard); - }); - connect(menu->addAction(tr("Copy image URL to clipboard")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::CopyImageUrlToClipboard); - }); - if(!ctxdata.mediaUrl().isEmpty()) { - if(this->url() != ctxdata.mediaUrl()) { - connect(menu->addAction(tr("Open image")), &QAction::triggered, this, [this, ctxdata]() { - load(ctxdata.mediaUrl()); - }); - connect(menu->addAction(tr("Open image in new tab")), &QAction::triggered, this, [this, ctxdata]() { - createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.mediaUrl()); - }); - } - connect(menu->addAction(tr("Save image")), &QAction::triggered, this, [this, ctxdata]() { - page()->download(ctxdata.mediaUrl()); - }); - } - - } else { - delete menu; - menu = page()->createStandardContextMenu(); - } - - if(!ctxdata.linkUrl().isEmpty()) { - menu->addSeparator(); - connect(menu->addAction(tr("Open link in new tab")), &QAction::triggered, this, [this, ctxdata]() { - createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.linkUrl()); - }); - - auto *newTabMenu = menu->addMenu(tr("Open link in new tab with profile")); - profileMenu(newTabMenu, [this, ctxdata](WebProfile *profile) { - auto *view = this->createWindow(QWebEnginePage::WebBrowserTab); - view->setProfile(profile); - view->load(ctxdata.linkUrl()); - }); - - connect(menu->addAction(tr("Open link in new window")), &QAction::triggered, this, [this, ctxdata]() { - createWindow(QWebEnginePage::WebBrowserWindow)->load(ctxdata.linkUrl()); - }); - - connect(menu->addAction(tr("Copy link address")), &QAction::triggered, this, [this]() { - page()->triggerAction(QWebEnginePage::CopyLinkToClipboard); - }); - } - - // zoom widget - { - menu->addSeparator(); - - auto *zoomSlider = new QSlider(Qt::Horizontal); - zoomSlider->setMinimum(5); - zoomSlider->setMaximum(50); - zoomSlider->setValue(zoomFactor() * 10); - - auto *zoomAction = menu->addAction(tr("Zoom: %1x").arg(zoomFactor())); - connect(zoomAction, &QAction::triggered, this, [zoomSlider]() { - zoomSlider->setValue(10); - }); - - connect(zoomSlider, &QSlider::valueChanged, this, [this, zoomAction](int value) { - zoomAction->setText(tr("Zoom: %1x").arg(static_cast<qreal>(value) / 10)); - setZoomFactor(static_cast<qreal>(value) / 10); - }); - - auto *zoomWidgetAction = new QWidgetAction(this); - zoomWidgetAction->setDefaultWidget(zoomSlider); - - menu->addAction(zoomWidgetAction); - } - -#ifdef QT_DEBUG - { - menu->addSeparator(); - auto *autofillAction = menu->addAction(tr("Autofill form")); - connect(autofillAction, &QAction::triggered, this, [this]() { - Wallet::autocompleteForm(this); - }); - }; -#endif - - menu->setMinimumWidth(250); + auto *menu = new WebViewContextMenu(this); + //const auto ctxdata = page()->contextMenuData(); menu->exec(event->globalPos()); } diff --git a/src/webengine/webview.h b/src/webengine/webview.h index 6fcbe3a..e5cd7fb 100644 --- a/src/webengine/webview.h +++ b/src/webengine/webview.h @@ -14,9 +14,13 @@ class WebProfile; class SubWindow; +class WebViewContextMenu; class WebView : public QWebEngineView { + friend class WebViewContextMenu; + Q_OBJECT + public: explicit WebView(WebProfile *profile = nullptr, QWidget *parent = nullptr); ~WebView() = default; diff --git a/src/webengine/webviewcontextmenu.cpp b/src/webengine/webviewcontextmenu.cpp new file mode 100644 index 0000000..856ff9c --- /dev/null +++ b/src/webengine/webviewcontextmenu.cpp @@ -0,0 +1,227 @@ +/* + * 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 "webviewcontextmenu.h" +#include "wallet/wallet.h" +#include "webprofilemanager.h" +#include "webview.h" +#include <QContextMenuEvent> +#include <QDialog> +#include <QMenu> +#include <QSlider> +#include <QStatusBar> +#include <QStyle> +#include <QToolButton> +#include <QVBoxLayout> +#include <QWebEngineContextMenuData> +#include <QWebEngineHistory> +#include <QWidgetAction> + +inline QAction *historyAction(QWebEngineView *view, const QWebEngineHistoryItem &item) +{ + QAction *action = new QAction(view); + if(item.title().isEmpty()) + action->setText(item.url().toString()); + else + action->setText(QObject::tr("%1 (%2)").arg(item.title(), item.url().toString())); + + QObject::connect(action, &QAction::triggered, view, [view, item]() { + view->history()->goToItem(item); + }); + return action; +} + +WebViewContextMenu::WebViewContextMenu(WebView *view) + : QMenu(view) +{ + setMinimumWidth(250); + + auto *navButtons = new QWidgetAction(this); + + auto *buttons = new QWidget(this); + auto *buttonsLayout = new QHBoxLayout; + buttonsLayout->setContentsMargins(8, 0, 8, 0); + buttonsLayout->setSpacing(2); + + auto *backButton = new QToolButton(this); + backButton->setEnabled(view->history()->canGoBack()); + backButton->setIcon(style()->standardIcon(QStyle::SP_ArrowBack)); + connect(backButton, &QToolButton::clicked, view, [this, view]() { + view->back(); + this->close(); + }); + buttonsLayout->addWidget(backButton); + + auto *forwardButton = new QToolButton(this); + forwardButton->setEnabled(view->history()->canGoForward()); + forwardButton->setIcon(style()->standardIcon(QStyle::SP_ArrowForward)); + connect(forwardButton, &QToolButton::clicked, view, [this, view]() { + view->forward(); + this->close(); + }); + buttonsLayout->addWidget(forwardButton); + + auto *refreshButton = new QToolButton(this); + refreshButton->setIcon(style()->standardIcon(QStyle::SP_BrowserReload)); + connect(refreshButton, &QToolButton::clicked, view, [view, this]() { + view->reload(); + this->close(); + }); + buttonsLayout->addWidget(refreshButton); + + buttonsLayout->addStretch(); + + auto *muteButton = new QToolButton(this); + muteButton->setCheckable(true); + muteButton->setChecked(view->page()->isAudioMuted()); + QIcon muteIcon; + muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolume), QIcon::Normal, QIcon::Off); + muteIcon.addPixmap(style()->standardPixmap(QStyle::SP_MediaVolumeMuted), QIcon::Normal, QIcon::On); + muteButton->setIcon(muteIcon); + connect(muteButton, &QToolButton::clicked, view, [view, this](bool checked) { + view->page()->setAudioMuted(checked); + this->close(); + }); + buttonsLayout->addWidget(muteButton); + + buttons->setLayout(buttonsLayout); + navButtons->setDefaultWidget(buttons); + + this->addAction(navButtons); + this->addSeparator(); + + const auto ctxdata = view->page()->contextMenuData(); + + if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeNone) { + auto *backMenu = this->addMenu(tr("Back")); + if(!view->history()->canGoBack()) { + backMenu->setEnabled(false); + } else { + connect(backMenu, &QMenu::aboutToShow, view, [view, backMenu]() { + backMenu->clear(); + const auto backItems = view->history()->backItems(10); + for(const QWebEngineHistoryItem &item : backItems) { + backMenu->addAction(historyAction(view, item)); + } + }); + } + + auto *forwardMenu = this->addMenu(tr("Forward")); + if(!view->history()->canGoForward()) { + forwardMenu->setEnabled(false); + } else { + connect(forwardMenu, &QMenu::aboutToShow, view, [view, forwardMenu]() { + forwardMenu->clear(); + const auto forwardItems = view->history()->forwardItems(10); + for(const QWebEngineHistoryItem &item : forwardItems) { + forwardMenu->addAction(historyAction(view, item)); + } + }); + } + + connect(this->addAction(tr("Reload")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::Reload); + }); + connect(this->addAction(tr("Reload and bypass Cache")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::ReloadAndBypassCache); + }); + + this->addSeparator(); + + connect(this->addAction(tr("Select All")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::SelectAll); + }); + connect(this->addAction(tr("Clear Selection")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::Unselect); + }); + connect(this->addAction(tr("Copy to clipboard")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::Copy); + }); + + } else if(ctxdata.mediaType() == QWebEngineContextMenuData::MediaTypeImage) { + connect(this->addAction(tr("Copy image to clipboard")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::CopyImageToClipboard); + }); + connect(this->addAction(tr("Copy image URL to clipboard")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::CopyImageUrlToClipboard); + }); + if(!ctxdata.mediaUrl().isEmpty()) { + if(view->url() != ctxdata.mediaUrl()) { + connect(this->addAction(tr("Open image")), &QAction::triggered, view, [view, ctxdata]() { + view->load(ctxdata.mediaUrl()); + }); + connect(this->addAction(tr("Open image in new tab")), &QAction::triggered, view, [view, ctxdata]() { + view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.mediaUrl()); + }); + } + connect(this->addAction(tr("Save image")), &QAction::triggered, view, [view, ctxdata]() { + view->page()->download(ctxdata.mediaUrl()); + }); + } + + } else { + addMenu(view->page()->createStandardContextMenu()); + } + + if(!ctxdata.linkUrl().isEmpty()) { + this->addSeparator(); + connect(this->addAction(tr("Open link in new tab")), &QAction::triggered, view, [view, ctxdata]() { + view->createWindow(QWebEnginePage::WebBrowserTab)->load(ctxdata.linkUrl()); + }); + + auto *newTabMenu = this->addMenu(tr("Open link in new tab with profile")); + profileMenu(newTabMenu, [view, ctxdata](WebProfile *profile) { + auto *v = view->createWindow(QWebEnginePage::WebBrowserTab); + v->setProfile(profile); + v->load(ctxdata.linkUrl()); + }); + + connect(this->addAction(tr("Open link in new window")), &QAction::triggered, view, [view, ctxdata]() { + view->createWindow(QWebEnginePage::WebBrowserWindow)->load(ctxdata.linkUrl()); + }); + + connect(this->addAction(tr("Copy link address")), &QAction::triggered, view, [view]() { + view->page()->triggerAction(QWebEnginePage::CopyLinkToClipboard); + }); + } + + // zoom widget + { + this->addSeparator(); + + auto *zoomSlider = new QSlider(Qt::Horizontal); + zoomSlider->setMinimum(5); + zoomSlider->setMaximum(50); + zoomSlider->setValue(view->zoomFactor() * 10); + + auto *zoomAction = this->addAction(tr("Zoom: %1x").arg(view->zoomFactor())); + connect(zoomAction, &QAction::triggered, view, [zoomSlider]() { + zoomSlider->setValue(10); + }); + + connect(zoomSlider, &QSlider::valueChanged, view, [view, zoomAction](int value) { + zoomAction->setText(tr("Zoom: %1x").arg(static_cast<qreal>(value) / 10)); + view->setZoomFactor(static_cast<qreal>(value) / 10); + }); + + auto *zoomWidgetAction = new QWidgetAction(this); + zoomWidgetAction->setDefaultWidget(zoomSlider); + + this->addAction(zoomWidgetAction); + } + +#ifndef NDEBUG + { + this->addSeparator(); + auto *autofillAction = this->addAction(tr("Autofill form")); + connect(autofillAction, &QAction::triggered, view, [view]() { + Wallet::autocompleteForm(view); + }); + }; +#endif +} diff --git a/src/webengine/webviewcontextmenu.h b/src/webengine/webviewcontextmenu.h new file mode 100644 index 0000000..881670a --- /dev/null +++ b/src/webengine/webviewcontextmenu.h @@ -0,0 +1,21 @@ +/* + * 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_WEBVIEWCONTEXTMENU_H +#define SMOLBOTE_WEBVIEWCONTEXTMENU_H + +#include <QMenu> + +class WebView; +class WebViewContextMenu : public QMenu +{ +public: + explicit WebViewContextMenu(WebView *view); +}; + +#endif // SMOLBOTE_WEBVIEWCONTEXTMENU_H |