diff options
author | Martin Negyokru <negyokru@inf.u-szeged.hu> | 2022-05-10 13:56:41 +0200 |
---|---|---|
committer | Martin Negyokru <negyokru@inf.u-szeged.hu> | 2022-12-12 18:31:33 +0100 |
commit | 117ed7f5c2bde195dd15a4b6b2cf9c03be088e1e (patch) | |
tree | d83b515c7593205804e5ee5d2db1aadf7c33ccf7 /src | |
parent | 48fa0fb2c32df11f0e8df0b8a46d6eddec4f46f3 (diff) |
Add touchSelectionMenu for widgets
[ChangeLog][QWebEngineWidgets] Added touch selection menu.
Task-number: QTBUG-100418
Task-number: QTBUG-91712
Change-Id: Iffb994e429b92389e64cd6a93a0b034d6aa07f66
Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/core/api/qwebenginepage.cpp | 20 | ||||
-rw-r--r-- | src/core/api/qwebenginepage_p.h | 8 | ||||
-rw-r--r-- | src/core/render_widget_host_view_qt.cpp | 3 | ||||
-rw-r--r-- | src/webenginewidgets/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/webenginewidgets/api/qwebengineview.cpp | 38 | ||||
-rw-r--r-- | src/webenginewidgets/api/qwebengineview_p.h | 6 | ||||
-rw-r--r-- | src/webenginewidgets/ui/touchselectionmenuwidget.cpp | 117 | ||||
-rw-r--r-- | src/webenginewidgets/ui/touchselectionmenuwidget_p.h | 52 |
8 files changed, 243 insertions, 2 deletions
diff --git a/src/core/api/qwebenginepage.cpp b/src/core/api/qwebenginepage.cpp index 1b640c940..ac44fad10 100644 --- a/src/core/api/qwebenginepage.cpp +++ b/src/core/api/qwebenginepage.cpp @@ -33,6 +33,7 @@ #include "render_widget_host_view_qt_delegate.h" #include "render_widget_host_view_qt_delegate_client.h" #include "render_widget_host_view_qt_delegate_item.h" +#include "touch_selection_menu_controller.h" #include "web_contents_adapter.h" #include <QAction> @@ -1658,6 +1659,25 @@ QWebEnginePagePrivate::createTouchHandleDelegate(const QMap<int, QImage> &images return view->createTouchHandleDelegate(images); } +void QWebEnginePagePrivate::showTouchSelectionMenu( + QtWebEngineCore::TouchSelectionMenuController *controller, const QRect &selectionBounds, + const QSize &handleSize) +{ + Q_UNUSED(handleSize); + + if (controller->buttonCount() == 1) { + controller->runContextMenu(); + return; + } + + view->showTouchSelectionMenu(controller, selectionBounds); +} + +void QWebEnginePagePrivate::hideTouchSelectionMenu() +{ + view->hideTouchSelectionMenu(); +} + void QWebEnginePagePrivate::lifecycleStateChanged(LifecycleState state) { Q_Q(QWebEnginePage); diff --git a/src/core/api/qwebenginepage_p.h b/src/core/api/qwebenginepage_p.h index a56494a32..800a8ab04 100644 --- a/src/core/api/qwebenginepage_p.h +++ b/src/core/api/qwebenginepage_p.h @@ -79,6 +79,9 @@ public: virtual void hideAutofillPopup() = 0; virtual QtWebEngineCore::TouchHandleDrawableDelegate * createTouchHandleDelegate(const QMap<int, QImage> &) = 0; + virtual void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, + const QRect &) = 0; + virtual void hideTouchSelectionMenu() = 0; }; class Q_WEBENGINECORE_PRIVATE_EXPORT QWebEnginePagePrivate : public QtWebEngineCore::WebContentsAdapterClient @@ -159,8 +162,9 @@ public: void printRequested() override; QtWebEngineCore::TouchHandleDrawableDelegate * createTouchHandleDelegate(const QMap<int, QImage> &) override; - void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, const QSize &) override { } - void hideTouchSelectionMenu() override { } + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, const QRect &, + const QSize &) override; + void hideTouchSelectionMenu() override; const QObject *holdingQObject() const override; ClientType clientType() override { return QtWebEngineCore::WebContentsAdapterClient::WidgetsClient; } void findTextFinished(const QWebEngineFindTextResult &result) override; diff --git a/src/core/render_widget_host_view_qt.cpp b/src/core/render_widget_host_view_qt.cpp index 0dd067cc5..5d8f15321 100644 --- a/src/core/render_widget_host_view_qt.cpp +++ b/src/core/render_widget_host_view_qt.cpp @@ -217,6 +217,9 @@ RenderWidgetHostViewQt::~RenderWidgetHostViewQt() if (text_input_manager_) text_input_manager_->RemoveObserver(this); + if (host()->delegate()) + m_touchSelectionControllerClient->resetControls(); + m_touchSelectionController.reset(); m_touchSelectionControllerClient.reset(); diff --git a/src/webenginewidgets/CMakeLists.txt b/src/webenginewidgets/CMakeLists.txt index b6de53a04..f859a774a 100644 --- a/src/webenginewidgets/CMakeLists.txt +++ b/src/webenginewidgets/CMakeLists.txt @@ -14,6 +14,7 @@ qt_internal_add_module(WebEngineWidgets qwebengine_accessible.cpp qwebengine_accessible.h ui/autofillpopupwidget.cpp ui/autofillpopupwidget_p.h ui/touchhandlewidget.cpp ui/touchhandlewidget_p.h + ui/touchselectionmenuwidget.cpp ui/touchselectionmenuwidget_p.h DEFINES QT_BUILD_WEBENGINEWIDGETS_LIB INCLUDE_DIRECTORIES diff --git a/src/webenginewidgets/api/qwebengineview.cpp b/src/webenginewidgets/api/qwebengineview.cpp index f75945942..bf975c96f 100644 --- a/src/webenginewidgets/api/qwebengineview.cpp +++ b/src/webenginewidgets/api/qwebengineview.cpp @@ -9,6 +9,7 @@ #include "qwebengine_accessible.h" #include "ui/autofillpopupwidget_p.h" #include "touchhandlewidget_p.h" +#include "touchselectionmenuwidget_p.h" #include <QtWebEngineCore/private/qwebenginepage_p.h> #include <QtWebEngineCore/qwebenginecontextmenurequest.h> @@ -18,6 +19,7 @@ #include "autofill_popup_controller.h" #include "color_chooser_controller.h" +#include "touch_selection_menu_controller.h" #include "web_contents_adapter.h" #include <QContextMenuEvent> @@ -1604,6 +1606,42 @@ QWebEngineViewPrivate::createTouchHandleDelegate(const QMap<int, QImage> &images return new QtWebEngineWidgetUI::TouchHandleWidget(q, images); } +void QWebEngineViewPrivate::hideTouchSelectionMenu() +{ + if (m_touchSelectionMenu) + m_touchSelectionMenu->close(); +} + +void QWebEngineViewPrivate::showTouchSelectionMenu( + QtWebEngineCore::TouchSelectionMenuController *controller, const QRect &selectionBounds) +{ + Q_ASSERT(m_touchSelectionMenu == nullptr); + Q_Q(QWebEngineView); + + // Do not show outside of view + QSize parentSize = q->nativeParentWidget() ? q->nativeParentWidget()->size() : q->size(); + if (selectionBounds.x() < 0 || selectionBounds.x() > parentSize.width() + || selectionBounds.y() < 0 || selectionBounds.y() > parentSize.height()) + return; + + m_touchSelectionMenu = new QtWebEngineWidgetUI::TouchSelectionMenuWidget(q, controller); + + const int kSpacingBetweenButtons = 2; + const int kMenuButtonMinWidth = 80; + const int kMenuButtonMinHeight = 40; + + int buttonCount = controller->buttonCount(); + int width = (kSpacingBetweenButtons * (buttonCount + 1)) + (kMenuButtonMinWidth * buttonCount); + int height = kMenuButtonMinHeight + kSpacingBetweenButtons; + int x = (selectionBounds.x() + selectionBounds.x() + selectionBounds.width() - width) / 2; + int y = selectionBounds.y() - height - 2; + + QPoint pos = q->mapToGlobal(QPoint(x, y)); + + m_touchSelectionMenu->setGeometry(pos.x(), pos.y(), width, height); + m_touchSelectionMenu->show(); +} + QT_END_NAMESPACE #include "moc_qwebengineview.cpp" diff --git a/src/webenginewidgets/api/qwebengineview_p.h b/src/webenginewidgets/api/qwebengineview_p.h index 9e0a40799..7c0f3fe60 100644 --- a/src/webenginewidgets/api/qwebengineview_p.h +++ b/src/webenginewidgets/api/qwebengineview_p.h @@ -25,11 +25,13 @@ class QWebEngineContextMenuRequest; class WebEngineQuickWidget; class RenderWidgetHostViewQtDelegate; class RenderWidgetHostViewQtDelegateClient; +class TouchSelectionMenuController; } namespace QtWebEngineWidgetUI { class AutofillPopupWidget; class TouchHandleDrawableDelegate; +class TouchSelectionMenuWidget; } QT_BEGIN_NAMESPACE @@ -75,6 +77,9 @@ public: QtWebEngineCore::TouchHandleDrawableDelegate * createTouchHandleDelegate(const QMap<int, QImage> &images) override; + void showTouchSelectionMenu(QtWebEngineCore::TouchSelectionMenuController *, + const QRect &) override; + void hideTouchSelectionMenu() override; QWebEngineViewPrivate(); virtual ~QWebEngineViewPrivate(); static void bindPageAndView(QWebEnginePage *page, QWebEngineView *view); @@ -93,6 +98,7 @@ public: mutable bool m_ownsPage; QWebEngineContextMenuRequest *m_contextRequest; QScopedPointer<QtWebEngineWidgetUI::AutofillPopupWidget> m_autofillPopupWidget; + QPointer<QtWebEngineWidgetUI::TouchSelectionMenuWidget> m_touchSelectionMenu; }; class QContextMenuBuilder : public QtWebEngineCore::RenderViewContextMenuQt diff --git a/src/webenginewidgets/ui/touchselectionmenuwidget.cpp b/src/webenginewidgets/ui/touchselectionmenuwidget.cpp new file mode 100644 index 000000000..ff69fe84b --- /dev/null +++ b/src/webenginewidgets/ui/touchselectionmenuwidget.cpp @@ -0,0 +1,117 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "touchselectionmenuwidget_p.h" +#include "qwebengineview.h" + +#include "touch_selection_menu_controller.h" + +#include <QBoxLayout> +#include <QEvent> + +namespace QtWebEngineWidgetUI { +namespace { +// The Widgets module is built with rtti in developer-build while Core is not. +// The MOC will throw "undefined reference to typeinfo..." errors if we connect slots +// from QtWebEngineCore via function pointers, because it expects rtti on them. +// To workaround this we use lambdas. +void connectButton(TouchSelectionMenuWidget::TouchButton *button, std::function<void()> callback) +{ + QObject::connect(button, &QPushButton::clicked, std::move(callback)); +} +} // namespace + +TouchSelectionMenuWidget::TouchButton::TouchButton(QString name, QWidget *parent) + : QPushButton(name, parent) +{ + setAttribute(Qt::WA_AcceptTouchEvents, true); +} + +TouchSelectionMenuWidget::TouchButton::~TouchButton() +{ +} + +bool TouchSelectionMenuWidget::TouchButton::event(QEvent *ev) +{ + switch (ev->type()) { + case QEvent::TouchBegin: + ev->accept(); + return true; + case QEvent::TouchEnd: + Q_EMIT clicked(); + ev->accept(); + return true; + default: + break; + } + + return QPushButton::event(ev); +} + +TouchSelectionMenuWidget::TouchSelectionMenuWidget( + QWebEngineView *view, QtWebEngineCore::TouchSelectionMenuController *controller) + : QWidget(view, + Qt::Window | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint + | Qt::WindowDoesNotAcceptFocus) +{ + setAttribute(Qt::WA_AcceptTouchEvents, true); + setAttribute(Qt::WA_TransparentForMouseEvents, true); + setAttribute(Qt::WA_DeleteOnClose, true); + + bool cutEnabled = + controller->isCommandEnabled(QtWebEngineCore::TouchSelectionMenuController::Cut); + bool copyEnabled = + controller->isCommandEnabled(QtWebEngineCore::TouchSelectionMenuController::Copy); + bool pasteEnabled = + controller->isCommandEnabled(QtWebEngineCore::TouchSelectionMenuController::Paste); + + QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QHBoxLayout *layout = new QHBoxLayout(); + + if (cutEnabled) { + TouchButton *button = new TouchButton(tr("Cut"), this); + button->setSizePolicy(policy); + layout->addWidget(button); + connectButton(button, [controller]() { controller->cut(); }); + } + + if (copyEnabled) { + TouchButton *button = new TouchButton(tr("Copy"), this); + button->setSizePolicy(policy); + layout->addWidget(button); + connectButton(button, [controller]() { controller->copy(); }); + } + + if (pasteEnabled) { + TouchButton *button = new TouchButton(tr("Paste"), this); + button->setSizePolicy(policy); + layout->addWidget(button); + connectButton(button, [controller]() { controller->paste(); }); + } + + TouchButton *button = new TouchButton(tr("..."), this); + button->setSizePolicy(policy); + layout->addWidget(button); + connectButton(button, [controller]() { controller->runContextMenu(); }); + + layout->setSpacing(2); + layout->setSizeConstraint(QLayout::SetMinimumSize); + layout->setContentsMargins(0, 0, 0, 0); + setLayout(layout); + + nativeParentWidget()->installEventFilter(this); +} + +TouchSelectionMenuWidget::~TouchSelectionMenuWidget() +{ +} + +bool TouchSelectionMenuWidget::eventFilter(QObject *obj, QEvent *ev) +{ + // Close the menu if the window is moved + if (ev->type() == QEvent::Move) + close(); + + return QWidget::eventFilter(obj, ev); +} +} // QtWebEngineWidgetUI diff --git a/src/webenginewidgets/ui/touchselectionmenuwidget_p.h b/src/webenginewidgets/ui/touchselectionmenuwidget_p.h new file mode 100644 index 000000000..1f822023b --- /dev/null +++ b/src/webenginewidgets/ui/touchselectionmenuwidget_p.h @@ -0,0 +1,52 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef TOUCHSELECTIONMENUWIDGET_P_H +#define TOUCHSELECTIONMENUWIDGET_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QPushButton> +#include <QWidget> + +namespace QtWebEngineCore { +class TouchSelectionMenuController; +} + +QT_BEGIN_NAMESPACE +class QWebEngineView; +QT_END_NAMESPACE + +namespace QtWebEngineWidgetUI { +class TouchSelectionMenuWidget : public QWidget +{ +public: + class TouchButton : public QPushButton + { + public: + TouchButton(QString name, QWidget *parent); + ~TouchButton(); + + protected: + bool event(QEvent *ev) override; + }; + + TouchSelectionMenuWidget(QWebEngineView *view, + QtWebEngineCore::TouchSelectionMenuController *controller); + ~TouchSelectionMenuWidget(); + +protected: + bool eventFilter(QObject *obj, QEvent *ev) override; +}; +} // namespace QtWebEngineWidgetUI + +#endif // TOUCHSELECTIONMENUWIDGET_P_H |