From 4d4fa829cd3ebeb284e691d7d928b67366f3cbf0 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Fri, 8 May 2015 16:05:17 +0200 Subject: Extend context menu actions Implements the several missing context menu actions for navigation and image and media handling. Change-Id: Ib8ea8311ea291fe2f98e509bc6f4034a5e0389c9 Reviewed-by: Andras Becsi --- src/core/clipboard_qt.cpp | 2 +- src/core/type_conversion.h | 5 + src/core/web_contents_adapter.cpp | 19 +++ src/core/web_contents_adapter.h | 12 ++ src/core/web_contents_adapter_client.h | 44 +++++ src/core/web_contents_view_qt.cpp | 24 +++ src/webenginewidgets/api/qwebenginepage.cpp | 183 ++++++++++++++++++--- src/webenginewidgets/api/qwebenginepage.h | 14 ++ .../doc/src/qwebenginepage_lgpl.qdoc | 13 ++ 9 files changed, 292 insertions(+), 24 deletions(-) (limited to 'src') diff --git a/src/core/clipboard_qt.cpp b/src/core/clipboard_qt.cpp index f7ca9764d..027a93bac 100644 --- a/src/core/clipboard_qt.cpp +++ b/src/core/clipboard_qt.cpp @@ -331,7 +331,7 @@ SkBitmap ClipboardQt::ReadImage(ui::ClipboardType type) const const QMimeData *mimeData = QGuiApplication::clipboard()->mimeData(type == ui::CLIPBOARD_TYPE_COPY_PASTE ? QClipboard::Clipboard : QClipboard::Selection); QImage image = qvariant_cast(mimeData->imageData()); - Q_ASSERT(image.format() == QImage::Format_ARGB32); + image = image.convertToFormat(QImage::Format_ARGB32); SkBitmap bitmap; bitmap.setInfo(SkImageInfo::MakeN32(image.width(), image.height(), kOpaque_SkAlphaType)); bitmap.setPixels(const_cast(image.constBits())); diff --git a/src/core/type_conversion.h b/src/core/type_conversion.h index 66fcd4dd0..3f5575a47 100644 --- a/src/core/type_conversion.h +++ b/src/core/type_conversion.h @@ -92,6 +92,11 @@ inline QPoint toQt(const gfx::Point &point) return QPoint(point.x(), point.y()); } +inline gfx::Point toGfx(const QPoint& point) +{ + return gfx::Point(point.x(), point.y()); +} + inline QRect toQt(const gfx::Rect &rect) { return QRect(rect.x(), rect.y(), rect.width(), rect.height()); diff --git a/src/core/web_contents_adapter.cpp b/src/core/web_contents_adapter.cpp index f0b0a0d94..ec7928c71 100644 --- a/src/core/web_contents_adapter.cpp +++ b/src/core/web_contents_adapter.cpp @@ -758,6 +758,25 @@ void WebContentsAdapter::updateWebPreferences(const content::WebPreferences & we d->webContents->GetRenderViewHost()->UpdateWebkitPreferences(webPreferences); } +void WebContentsAdapter::copyImageAt(const QPoint &location) +{ + Q_D(WebContentsAdapter); + d->webContents->GetRenderViewHost()->CopyImageAt(location.x(), location.y()); +} + +ASSERT_ENUMS_MATCH(WebContentsAdapter::MediaPlayerNoAction, blink::WebMediaPlayerAction::Unknown) +ASSERT_ENUMS_MATCH(WebContentsAdapter::MediaPlayerPlay, blink::WebMediaPlayerAction::Play) +ASSERT_ENUMS_MATCH(WebContentsAdapter::MediaPlayerMute, blink::WebMediaPlayerAction::Mute) +ASSERT_ENUMS_MATCH(WebContentsAdapter::MediaPlayerLoop, blink::WebMediaPlayerAction::Loop) +ASSERT_ENUMS_MATCH(WebContentsAdapter::MediaPlayerControls, blink::WebMediaPlayerAction::Controls) + +void WebContentsAdapter::executeMediaPlayerActionAt(const QPoint &location, MediaPlayerAction action, bool enable) +{ + Q_D(WebContentsAdapter); + blink::WebMediaPlayerAction blinkAction((blink::WebMediaPlayerAction::Type)action, enable); + d->webContents->GetRenderViewHost()->ExecuteMediaPlayerActionAtLocation(toGfx(location), blinkAction); +} + void WebContentsAdapter::wasShown() { Q_D(WebContentsAdapter); diff --git a/src/core/web_contents_adapter.h b/src/core/web_contents_adapter.h index 7f644cdd2..2e65b86c1 100644 --- a/src/core/web_contents_adapter.h +++ b/src/core/web_contents_adapter.h @@ -112,6 +112,18 @@ public: void stopFinding(); void updateWebPreferences(const content::WebPreferences &webPreferences); + // Must match blink::WebMediaPlayerAction::Type. + enum MediaPlayerAction { + MediaPlayerNoAction, + MediaPlayerPlay, + MediaPlayerMute, + MediaPlayerLoop, + MediaPlayerControls, + MediaPlayerTypeLast = MediaPlayerControls + }; + void copyImageAt(const QPoint &location); + void executeMediaPlayerActionAt(const QPoint &location, MediaPlayerAction action, bool enable); + void wasShown(); void wasHidden(); void grantMediaAccessPermission(const QUrl &securityOrigin, WebContentsAdapterClient::MediaRequestFlags flags); diff --git a/src/core/web_contents_adapter_client.h b/src/core/web_contents_adapter_client.h index 886f8bcc6..05a31c0cd 100644 --- a/src/core/web_contents_adapter_client.h +++ b/src/core/web_contents_adapter_client.h @@ -66,10 +66,54 @@ class WebEngineSettings; class WebEngineContextMenuData { public: + WebEngineContextMenuData() + : mediaType(MediaTypeNone) + , hasImageContent(false) + , mediaFlags(0) + { + } + + // Must match blink::WebContextMenuData::MediaType: + enum MediaType { + // No special node is in context. + MediaTypeNone, + // An image node is selected. + MediaTypeImage, + // A video node is selected. + MediaTypeVideo, + // An audio node is selected. + MediaTypeAudio, + // A canvas node is selected. + MediaTypeCanvas, + // A file node is selected. + MediaTypeFile, + // A plugin node is selected. + MediaTypePlugin, + MediaTypeLast = MediaTypePlugin + }; + // Must match blink::WebContextMenuData::MediaFlags: + enum MediaFlags { + MediaNone = 0x0, + MediaInError = 0x1, + MediaPaused = 0x2, + MediaMuted = 0x4, + MediaLoop = 0x8, + MediaCanSave = 0x10, + MediaHasAudio = 0x20, + MediaCanToggleControls = 0x40, + MediaControls = 0x80, + MediaCanPrint = 0x100, + MediaCanRotate = 0x200, + }; + QPoint pos; QUrl linkUrl; QString linkText; QString selectedText; + QUrl mediaUrl; + MediaType mediaType; + bool hasImageContent; + uint mediaFlags; // Some likely candidates for future additions as we add support for the related actions: // bool isImageBlocked; // bool isEditable; diff --git a/src/core/web_contents_view_qt.cpp b/src/core/web_contents_view_qt.cpp index 932170634..e7764fbcb 100644 --- a/src/core/web_contents_view_qt.cpp +++ b/src/core/web_contents_view_qt.cpp @@ -110,6 +110,26 @@ void WebContentsViewQt::SetInitialFocus() Focus(); } +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeNone, blink::WebContextMenuData::MediaTypeNone) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeImage, blink::WebContextMenuData::MediaTypeImage) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeVideo, blink::WebContextMenuData::MediaTypeVideo) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeAudio, blink::WebContextMenuData::MediaTypeAudio) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeCanvas, blink::WebContextMenuData::MediaTypeCanvas) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypeFile, blink::WebContextMenuData::MediaTypeFile) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaTypePlugin, blink::WebContextMenuData::MediaTypePlugin) + +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaNone, blink::WebContextMenuData::MediaNone) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaInError, blink::WebContextMenuData::MediaInError) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaPaused, blink::WebContextMenuData::MediaPaused) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaMuted, blink::WebContextMenuData::MediaMuted) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaLoop, blink::WebContextMenuData::MediaLoop) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaCanSave, blink::WebContextMenuData::MediaCanSave) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaHasAudio, blink::WebContextMenuData::MediaHasAudio) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaCanToggleControls, blink::WebContextMenuData::MediaCanToggleControls) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaControls, blink::WebContextMenuData::MediaControls) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaCanPrint, blink::WebContextMenuData::MediaCanPrint) +ASSERT_ENUMS_MATCH(WebEngineContextMenuData::MediaCanRotate, blink::WebContextMenuData::MediaCanRotate) + static WebEngineContextMenuData fromParams(const content::ContextMenuParams ¶ms) { WebEngineContextMenuData ret; @@ -117,6 +137,10 @@ static WebEngineContextMenuData fromParams(const content::ContextMenuParams &par ret.linkUrl = toQt(params.link_url); ret.linkText = toQt(params.link_text.data()); ret.selectedText = toQt(params.selection_text.data()); + ret.mediaUrl = toQt(params.src_url); + ret.mediaType = (WebEngineContextMenuData::MediaType)params.media_type; + ret.hasImageContent = params.has_image_contents; + ret.mediaFlags = params.media_flags; return ret; } diff --git a/src/webenginewidgets/api/qwebenginepage.cpp b/src/webenginewidgets/api/qwebenginepage.cpp index 04de59df0..9340bf3aa 100644 --- a/src/webenginewidgets/api/qwebenginepage.cpp +++ b/src/webenginewidgets/api/qwebenginepage.cpp @@ -55,6 +55,7 @@ #include #include #include +#include #include #include #include @@ -615,6 +616,39 @@ QAction *QWebEnginePage::action(WebAction action) const case PasteAndMatchStyle: text = tr("Paste and Match Style"); break; + case OpenLinkInThisWindow: + text = tr("Open Link in This Window"); + break; + case OpenLinkInNewWindow: + text = tr("Open Link in New Window"); + break; + case OpenLinkInNewTab: + text = tr("Open Link in New Tab"); + break; + case CopyLinkToClipboard: + text = tr("Copy Link URL"); + break; + case CopyImageToClipboard: + text = tr("Copy Image"); + break; + case CopyImageUrlToClipboard: + text = tr("Copy Image URL"); + break; + case CopyMediaUrlToClipboard: + text = tr("Copy Media URL"); + break; + case ToggleMediaControls: + text = tr("Toggle Media Controls"); + break; + case ToggleMediaLoop: + text = tr("Toggle Looping"); + break; + case ToggleMediaPlayPause: + text = tr("Toggle Play/Pause"); + break; + case ToggleMediaMute: + text = tr("Toggle Mute"); + break; default: break; } @@ -672,6 +706,104 @@ void QWebEnginePage::triggerAction(WebAction action, bool) case PasteAndMatchStyle: d->adapter->pasteAndMatchStyle(); break; + case OpenLinkInThisWindow: + if (d->m_menuData.linkUrl.isValid()) + setUrl(d->m_menuData.linkUrl); + break; + case OpenLinkInNewWindow: + if (d->m_menuData.linkUrl.isValid()) { + QWebEnginePage *newPage = createWindow(WebBrowserWindow); + if (newPage) + newPage->setUrl(d->m_menuData.linkUrl); + } + break; + case OpenLinkInNewTab: + if (d->m_menuData.linkUrl.isValid()) { + QWebEnginePage *newPage = createWindow(WebBrowserTab); + if (newPage) + newPage->setUrl(d->m_menuData.linkUrl); + } + break; + case CopyLinkToClipboard: + if (d->m_menuData.linkUrl.isValid()) { + QString urlString = d->m_menuData.linkUrl.toString(QUrl::FullyEncoded); + QString title = d->m_menuData.linkText.toHtmlEscaped(); + QMimeData *data = new QMimeData(); + data->setText(urlString); + QString html = QStringLiteral("") + title + QStringLiteral(""); + data->setHtml(html); + data->setUrls(QList() << d->m_menuData.linkUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case CopyImageToClipboard: + if (d->m_menuData.hasImageContent && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeImage || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeCanvas)) + { + d->adapter->copyImageAt(d->m_menuData.pos); + } + break; + case CopyImageUrlToClipboard: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeImage) { + QString urlString = d->m_menuData.mediaUrl.toString(QUrl::FullyEncoded); + QString title = d->m_menuData.linkText; + if (!title.isEmpty()) + title = QStringLiteral(" alt=\"%1\"").arg(title.toHtmlEscaped()); + QMimeData *data = new QMimeData(); + data->setText(urlString); + QString html = QStringLiteral(""); + data->setHtml(html); + data->setUrls(QList() << d->m_menuData.mediaUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case CopyMediaUrlToClipboard: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + QString urlString = d->m_menuData.mediaUrl.toString(QUrl::FullyEncoded); + QMimeData *data = new QMimeData(); + data->setText(urlString); + if (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio) + data->setHtml(QStringLiteral("")); + else + data->setHtml(QStringLiteral("")); + data->setUrls(QList() << d->m_menuData.mediaUrl); + qApp->clipboard()->setMimeData(data); + } + break; + case ToggleMediaControls: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaCanToggleControls) { + bool enable = !(d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaControls); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerControls, enable); + } + break; + case ToggleMediaLoop: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + bool enable = !(d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaLoop); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerLoop, enable); + } + break; + case ToggleMediaPlayPause: + if (d->m_menuData.mediaUrl.isValid() && + (d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeAudio || + d->m_menuData.mediaType == WebEngineContextMenuData::MediaTypeVideo)) + { + bool enable = (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaPaused); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerPlay, enable); + } + break; + case ToggleMediaMute: + if (d->m_menuData.mediaUrl.isValid() && d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaHasAudio) { + bool enable = (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaMuted); + d->adapter->executeMediaPlayerActionAt(d->m_menuData.pos, WebContentsAdapter::MediaPlayerMute, enable); + } + break; default: Q_UNREACHABLE(); } @@ -704,6 +836,7 @@ bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData if (!view) return false; + m_menuData = WebEngineContextMenuData(); QContextMenuEvent event(QContextMenuEvent::Mouse, data.pos, view->mapToGlobal(data.pos)); switch (view->contextMenuPolicy()) { case Qt::PreventContextMenu: @@ -728,7 +861,6 @@ bool QWebEnginePagePrivate::contextMenuRequested(const WebEngineContextMenuData } Q_ASSERT(view->d_func()->m_pendingContextMenuEvent); view->d_func()->m_pendingContextMenuEvent = false; - m_menuData = WebEngineContextMenuData(); return true; } @@ -821,21 +953,6 @@ public: qApp->clipboard()->setText(m_text); } }; - -class LoadUrlFunctor -{ - QWebEnginePage *m_page; - QUrl m_url; -public: - LoadUrlFunctor(QWebEnginePage *page, const QUrl &url) - : m_page(page) - , m_url(url) - {} - void operator()() const - { - m_page->load(m_url); - } -}; } QMenu *QWebEnginePage::createStandardContextMenu() @@ -844,6 +961,9 @@ QMenu *QWebEnginePage::createStandardContextMenu() QMenu *menu = new QMenu(d->view); QAction *action = 0; WebEngineContextMenuData contextMenuData(d->m_menuData); + if (!contextMenuData.linkText.isEmpty() && contextMenuData.linkUrl.isValid()) { + menu->addAction(QWebEnginePage::action(OpenLinkInThisWindow)); + } if (contextMenuData.selectedText.isEmpty()) { action = new QAction(QIcon::fromTheme(QStringLiteral("go-previous")), tr("&Back"), menu); connect(action, &QAction::triggered, d->view, &QWebEngineView::back); @@ -865,14 +985,31 @@ QMenu *QWebEnginePage::createStandardContextMenu() } if (!contextMenuData.linkText.isEmpty() && contextMenuData.linkUrl.isValid()) { - menu->addSeparator(); - action = new QAction(tr("Navigate to..."), menu); - connect(action, &QAction::triggered, LoadUrlFunctor(this, contextMenuData.linkUrl)); - menu->addAction(action); - action = new QAction(tr("Copy link address"), menu); - connect(action, &QAction::triggered, SaveToClipboardFunctor(contextMenuData.linkUrl.toString())); - menu->addAction(action); + menu->addAction(QWebEnginePage::action(CopyLinkToClipboard)); } + if (contextMenuData.mediaUrl.isValid()) { + switch (contextMenuData.mediaType) { + case WebEngineContextMenuData::MediaTypeImage: + menu->addAction(QWebEnginePage::action(CopyImageUrlToClipboard)); + // no break + case WebEngineContextMenuData::MediaTypeCanvas: + menu->addAction(QWebEnginePage::action(CopyImageToClipboard)); + break; + case WebEngineContextMenuData::MediaTypeAudio: + case WebEngineContextMenuData::MediaTypeVideo: + menu->addAction(QWebEnginePage::action(CopyMediaUrlToClipboard)); + menu->addAction(QWebEnginePage::action(ToggleMediaPlayPause)); + menu->addAction(QWebEnginePage::action(ToggleMediaLoop)); + if (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaHasAudio) + menu->addAction(QWebEnginePage::action(ToggleMediaMute)); + if (d->m_menuData.mediaFlags & WebEngineContextMenuData::MediaCanToggleControls) + menu->addAction(QWebEnginePage::action(ToggleMediaControls)); + break; + default: + break; + } + } + return menu; } diff --git a/src/webenginewidgets/api/qwebenginepage.h b/src/webenginewidgets/api/qwebenginepage.h index 4414ed1d5..4281a70a0 100644 --- a/src/webenginewidgets/api/qwebenginepage.h +++ b/src/webenginewidgets/api/qwebenginepage.h @@ -118,6 +118,20 @@ public: PasteAndMatchStyle, + OpenLinkInThisWindow, + OpenLinkInNewWindow, + OpenLinkInNewTab, + CopyLinkToClipboard, + + CopyImageToClipboard, + CopyImageUrlToClipboard, + + CopyMediaUrlToClipboard, + ToggleMediaControls, + ToggleMediaLoop, + ToggleMediaPlayPause, + ToggleMediaMute, + WebActionCount }; diff --git a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc index 3088a66b6..3c47d97df 100644 --- a/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc +++ b/src/webenginewidgets/doc/src/qwebenginepage_lgpl.qdoc @@ -98,6 +98,19 @@ \value SelectAll Selects all content. \value PasteAndMatchStyle Paste content from the clipboard with current style. + \value OpenLinkInThisWindow Open the current link in the current window. (Added in Qt 5.6) + \value OpenLinkInNewWindow Open the current link in a new window. (Added in Qt 5.6) + \value OpenLinkInNewTab Open the current link in a new tab. (Added in Qt 5.6) + \value CopyLinkToClipboard Copy the current link to the clipboard. (Added in Qt 5.6) + + \value CopyImageToClipboard Copy the clicked image to the clipboard. (Added in Qt 5.6) + \value CopyImageUrlToClipboard Copy the clicked image's URL to the clipboard. (Added in Qt 5.6) + \value CopyMediaUrlToClipboard Copy the hovered audio or video's URL to the clipboard. (Added in Qt 5.6) + \value ToggleMediaControls Toggles between showing and hiding the controls for the hovered audio or video element. (Added in Qt 5.6) + \value ToggleMediaLoop Toggles whether the hovered audio or video should loop on completetion or not. (Added in Qt 5.6) + \value ToggleMediaPlayPause Toggles the play/pause state of the hovered audio or video element. (Added in Qt 5.6) + \value ToggleMediaMute Mutes or unmutes the hovered audio or video element. (Added in Qt 5.6) + \omitvalue WebActionCount */ -- cgit v1.2.3