summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoerg Bornemann <joerg.bornemann@qt.io>2017-04-10 13:47:20 +0200
committerJoerg Bornemann <joerg.bornemann@qt.io>2017-04-25 18:18:30 +0000
commit3902b27ee40400db6cf596ca0db31b6497f0421b (patch)
tree774bb6e244482dd308b7488e91cfdba4a3eee665
parent3e7aab785a22e0bdf96d22e2ed03689c37554baa (diff)
Override shortcuts in HTML input fields
When users defined a single-letter short cut it was not possible to type this letter in HTML input fields. Fix this by accepting ShortcutOverride events whenever the web page is editing text. Use QInputControl::isCommonTextEditShortcut for Qt 5.9 and later. For the case where QtWebEngine is built against an older Qt a duplicated code path is used. Also, ensure users do not override web action short cuts. Task-number: QTBUG-59053 Change-Id: Ic26cf2a040a72b118273c6645c00b2913b995b0b Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
-rw-r--r--src/core/core_chromium.pri1
-rw-r--r--src/core/render_widget_host_view_qt_delegate.cpp113
-rw-r--r--src/core/render_widget_host_view_qt_delegate.h2
-rw-r--r--src/webengine/render_widget_host_view_qt_delegate_quick.cpp8
-rw-r--r--src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp21
-rw-r--r--tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp58
6 files changed, 198 insertions, 5 deletions
diff --git a/src/core/core_chromium.pri b/src/core/core_chromium.pri
index 9c1e9f0d..f13095bf 100644
--- a/src/core/core_chromium.pri
+++ b/src/core/core_chromium.pri
@@ -79,6 +79,7 @@ SOURCES = \
qrc_protocol_handler_qt.cpp \
render_view_observer_host_qt.cpp \
render_widget_host_view_qt.cpp \
+ render_widget_host_view_qt_delegate.cpp \
renderer/content_renderer_client_qt.cpp \
renderer/render_frame_observer_qt.cpp \
renderer/render_view_observer_qt.cpp \
diff --git a/src/core/render_widget_host_view_qt_delegate.cpp b/src/core/render_widget_host_view_qt_delegate.cpp
new file mode 100644
index 00000000..a8690043
--- /dev/null
+++ b/src/core/render_widget_host_view_qt_delegate.cpp
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtWebEngine module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "render_widget_host_view_qt_delegate.h"
+
+#include <QtCore/qvariant.h>
+#include <QtGui/qevent.h>
+
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+#include <QtGui/private/qinputcontrol_p.h>
+#endif
+
+static bool isCommonTextEditShortcut(const QKeyEvent *ke)
+{
+#if QT_VERSION >= QT_VERSION_CHECK(5, 9, 0)
+ return QInputControl::isCommonTextEditShortcut(ke);
+#else
+ if (ke->modifiers() == Qt::NoModifier
+ || ke->modifiers() == Qt::ShiftModifier
+ || ke->modifiers() == Qt::KeypadModifier) {
+ if (ke->key() < Qt::Key_Escape) {
+ return true;
+ } else {
+ switch (ke->key()) {
+ case Qt::Key_Return:
+ case Qt::Key_Enter:
+ case Qt::Key_Delete:
+ case Qt::Key_Home:
+ case Qt::Key_End:
+ case Qt::Key_Backspace:
+ case Qt::Key_Left:
+ case Qt::Key_Right:
+ case Qt::Key_Up:
+ case Qt::Key_Down:
+ case Qt::Key_Tab:
+ return true;
+ default:
+ break;
+ }
+ }
+ } else if (ke->matches(QKeySequence::Copy)
+ || ke->matches(QKeySequence::Paste)
+ || ke->matches(QKeySequence::Cut)
+ || ke->matches(QKeySequence::Redo)
+ || ke->matches(QKeySequence::Undo)
+ || ke->matches(QKeySequence::MoveToNextWord)
+ || ke->matches(QKeySequence::MoveToPreviousWord)
+ || ke->matches(QKeySequence::MoveToStartOfDocument)
+ || ke->matches(QKeySequence::MoveToEndOfDocument)
+ || ke->matches(QKeySequence::SelectNextWord)
+ || ke->matches(QKeySequence::SelectPreviousWord)
+ || ke->matches(QKeySequence::SelectStartOfLine)
+ || ke->matches(QKeySequence::SelectEndOfLine)
+ || ke->matches(QKeySequence::SelectStartOfBlock)
+ || ke->matches(QKeySequence::SelectEndOfBlock)
+ || ke->matches(QKeySequence::SelectStartOfDocument)
+ || ke->matches(QKeySequence::SelectEndOfDocument)
+ || ke->matches(QKeySequence::SelectAll)
+ ) {
+ return true;
+ }
+ return false;
+#endif
+}
+
+namespace QtWebEngineCore {
+
+bool RenderWidgetHostViewQtDelegateClient::handleShortcutOverrideEvent(QKeyEvent *event)
+{
+ if (inputMethodQuery(Qt::ImEnabled).toBool() && isCommonTextEditShortcut(event)) {
+ event->accept();
+ return true;
+ }
+ return false;
+}
+
+} // namespace QtWebEngineCore
diff --git a/src/core/render_widget_host_view_qt_delegate.h b/src/core/render_widget_host_view_qt_delegate.h
index 6286596c..dda59a01 100644
--- a/src/core/render_widget_host_view_qt_delegate.h
+++ b/src/core/render_widget_host_view_qt_delegate.h
@@ -48,6 +48,7 @@
QT_BEGIN_NAMESPACE
class QCursor;
class QEvent;
+class QKeyEvent;
class QPainter;
class QSGLayer;
class QSGNode;
@@ -85,6 +86,7 @@ public:
virtual void windowChanged() = 0;
virtual bool forwardEvent(QEvent *) = 0;
virtual QVariant inputMethodQuery(Qt::InputMethodQuery query) = 0;
+ virtual bool handleShortcutOverrideEvent(QKeyEvent *event);
};
class QWEBENGINE_EXPORT RenderWidgetHostViewQtDelegate {
diff --git a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
index b3348b43..749a2e0d 100644
--- a/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
+++ b/src/webengine/render_widget_host_view_qt_delegate_quick.cpp
@@ -241,10 +241,12 @@ void RenderWidgetHostViewQtDelegateQuick::inputMethodStateChanged(bool editorVis
bool RenderWidgetHostViewQtDelegateQuick::event(QEvent *event)
{
if (event->type() == QEvent::ShortcutOverride) {
- if (editorActionForKeyEvent(static_cast<QKeyEvent*>(event)) != QQuickWebEngineView::NoWebAction) {
- event->accept();
+ QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
+ if (m_client->handleShortcutOverrideEvent(keyEvent))
return true;
- }
+ if (editorActionForKeyEvent(keyEvent) != QQuickWebEngineView::NoWebAction)
+ event->accept();
+ return true;
}
if (event->type() == QEvent::NativeGesture)
diff --git a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
index fd58a070..c608ba2a 100644
--- a/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
+++ b/src/webenginewidgets/render_widget_host_view_qt_delegate_widget.cpp
@@ -59,6 +59,15 @@
namespace QtWebEngineCore {
+static bool handleShortcutOverrideEvent(RenderWidgetHostViewQtDelegateClient *client, QKeyEvent *ke)
+{
+ if (client->handleShortcutOverrideEvent(ke))
+ return true;
+ if (editorActionForKeyEvent(ke) != QWebEnginePage::NoWebAction)
+ ke->accept();
+ return true;
+}
+
class RenderWidgetHostViewQuickItem : public QQuickItem {
public:
RenderWidgetHostViewQuickItem(RenderWidgetHostViewQtDelegateClient *client) : m_client(client)
@@ -68,6 +77,14 @@ public:
setFocus(true);
}
protected:
+ bool event(QEvent *event) override
+ {
+ if (event->type() == QEvent::ShortcutOverride) {
+ handleShortcutOverrideEvent(m_client, static_cast<QKeyEvent *>(event));
+ return true;
+ }
+ return QQuickItem::event(event);
+ }
void focusInEvent(QFocusEvent *event) override
{
m_client->forwardEvent(event);
@@ -437,8 +454,8 @@ bool RenderWidgetHostViewQtDelegateWidget::event(QEvent *event)
// We forward focus events later, once they have made it to the m_rootItem.
return QQuickWidget::event(event);
case QEvent::ShortcutOverride:
- if (editorActionForKeyEvent(static_cast<QKeyEvent*>(event)) != QWebEnginePage::NoWebAction) {
- event->accept();
+ if (event->type() == QEvent::ShortcutOverride) {
+ handleShortcutOverrideEvent(m_client, static_cast<QKeyEvent *>(event));
return true;
}
break;
diff --git a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
index 78190622..ce88ace1 100644
--- a/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
+++ b/tests/auto/widgets/qwebengineview/tst_qwebengineview.cpp
@@ -42,6 +42,7 @@
#include <QTcpServer>
#include <QTcpSocket>
#include <QStyle>
+#include <QtWidgets/qaction.h>
#define VERIFY_INPUTMETHOD_HINTS(actual, expect) \
QVERIFY(actual == expect);
@@ -95,6 +96,7 @@ private Q_SLOTS:
void keyboardEvents();
void keyboardFocusAfterPopup();
void postData();
+ void inputFieldOverridesShortcuts();
void softwareInputPanel();
void inputMethods();
@@ -1302,6 +1304,62 @@ void tst_QWebEngineView::postData()
server.close();
}
+void tst_QWebEngineView::inputFieldOverridesShortcuts()
+{
+ bool actionTriggered = false;
+ QAction *action = new QAction;
+ action->setShortcut(Qt::Key_X);
+ connect(action, &QAction::triggered, [&actionTriggered] () { actionTriggered = true; });
+
+ QWebEngineView view;
+ view.addAction(action);
+
+ QSignalSpy loadFinishedSpy(&view, SIGNAL(loadFinished(bool)));
+ view.setHtml(QString("<html><body onload=\"input1=document.getElementById('input1')\">"
+ "<input id=\"dummy\" type=\"text\">"
+ "<input id=\"input1\" type=\"text\" value=\"x\">"
+ "</body></html>"));
+ QVERIFY(loadFinishedSpy.wait());
+
+ view.show();
+ QTest::qWaitForWindowActive(&view);
+
+ auto inputFieldValue = [&view] () -> QString {
+ return evaluateJavaScriptSync(view.page(),
+ "input1.value").toString();
+ };
+
+ // The input form is not focused. The action is triggered on pressing X.
+ QTest::keyClick(view.windowHandle(), Qt::Key_X);
+ QTRY_VERIFY(actionTriggered);
+ QCOMPARE(inputFieldValue(), QString("x"));
+
+ // The input form is focused. The action is not triggered, and the form's text changed.
+ evaluateJavaScriptSync(view.page(), "input1.focus();");
+ actionTriggered = false;
+ QTest::keyClick(view.windowHandle(), Qt::Key_Y);
+ QTRY_COMPARE(inputFieldValue(), QString("yx"));
+ QVERIFY(!actionTriggered);
+
+ // The input form is focused. Make sure we don't override all short cuts.
+ // A Ctrl-1 action is no default Qt key binding and should be triggerable.
+ action->setShortcut(Qt::CTRL + Qt::Key_1);
+ QTest::keyClick(view.windowHandle(), Qt::Key_1, Qt::ControlModifier);
+ QTRY_VERIFY(actionTriggered);
+ QCOMPARE(inputFieldValue(), QString("yx"));
+
+ // Remove focus from the input field. A QKeySequence::Copy action still must not be triggered.
+ evaluateJavaScriptSync(view.page(), "input1.blur();");
+ action->setShortcut(QKeySequence::Copy);
+ actionTriggered = false;
+ QTest::keyClick(view.windowHandle(), Qt::Key_C, Qt::ControlModifier);
+ // Add some text in the input field to ensure that the key event went through.
+ evaluateJavaScriptSync(view.page(), "input1.focus();");
+ QTest::keyClick(view.windowHandle(), Qt::Key_U);
+ QTRY_COMPARE(inputFieldValue(), QString("yux"));
+ QVERIFY(!actionTriggered);
+}
+
class TestInputContext : public QPlatformInputContext
{
public: