From baeb20b2e720f5c683cf3810116d827e1561c4b2 Mon Sep 17 00:00:00 2001
From: Michal Klocek <michal.klocek@theqtcompany.com>
Date: Mon, 13 Jun 2016 12:38:51 +0200
Subject: [PATCH] Add Qt Quick Controls 2 support for dialogs

QtQuickControls1 does not handle embedded
platforms too well. In case of eglfs platform
we crash badly - only one window is supported.
QtQuickControls2 on the other hand lacks the native
look on desktop. Therefore on desktop platforms
keep using QtQuickControls1, and on eglfs use
QtQuickControls2.

QtQuick.Dialogs are not implemented for QtQuickControls2,
moreover required authentication dialog and prompt
dialog are neither implemented in QtQuickControls1.
As a workaround  make new dialogs to give
consistent look and feel.

Replace close() with reject() signal in java script
prompt dialog to unify handling between qqc1 and qqc2

[ChangeLog][QtWebEngine][General] Qt WebEngine (QML)
now optionally uses Qt Quick 2 Controls to show standard dialogs.

Task-number: QTBUG-53467
Task-number: QTBUG-51177
Change-Id: I42f9506357bbb82d4f04465f30a18c8013439e25
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
---
 src/src.pro                                 |   6 +-
 src/webengine/api/qquickwebengineview.cpp   |  33 ++--
 src/webengine/api/qquickwebengineview_p_p.h |   3 +-
 src/webengine/ui/PromptDialog.qml           |   4 +
 src/webengine/ui2/AlertDialog.qml           |  98 ++++++++++++
 src/webengine/ui2/AuthenticationDialog.qml  | 135 ++++++++++++++++
 src/webengine/ui2/ConfirmDialog.qml         | 111 +++++++++++++
 src/webengine/ui2/Menu.qml                  |  57 +++++++
 src/webengine/ui2/MenuItem.qml              |  44 ++++++
 src/webengine/ui2/MenuSeparator.qml         |  42 +++++
 src/webengine/ui2/PromptDialog.qml          | 114 ++++++++++++++
 src/webengine/ui2/information.png           | Bin 0 -> 254 bytes
 src/webengine/ui2/qmldir                    |   2 +
 src/webengine/ui2/question.png              | Bin 0 -> 257 bytes
 src/webengine/ui2/ui2.pro                   |  17 ++
 src/webengine/ui_delegates_manager.cpp      | 164 +++++++++++++++-----
 src/webengine/ui_delegates_manager.h        |  37 ++++-
 sync.profile                                |   1 +
 18 files changed, 810 insertions(+), 58 deletions(-)
 create mode 100644 src/webengine/ui2/AlertDialog.qml
 create mode 100644 src/webengine/ui2/AuthenticationDialog.qml
 create mode 100644 src/webengine/ui2/ConfirmDialog.qml
 create mode 100644 src/webengine/ui2/Menu.qml
 create mode 100644 src/webengine/ui2/MenuItem.qml
 create mode 100644 src/webengine/ui2/MenuSeparator.qml
 create mode 100644 src/webengine/ui2/PromptDialog.qml
 create mode 100644 src/webengine/ui2/information.png
 create mode 100644 src/webengine/ui2/qmldir
 create mode 100644 src/webengine/ui2/question.png
 create mode 100644 src/webengine/ui2/ui2.pro

diff --git a/src/src.pro b/src/src.pro
index 64c1703fe..00e8301be 100644
--- a/src/src.pro
+++ b/src/src.pro
@@ -34,8 +34,10 @@ isQMLTestSupportApiEnabled() {
 
 # FIXME: We probably want a bit more control over config options to tweak what to build/ship or not.
 # Another example of where this could be necessary is to make it easy to build proprietery codecs support.
-!contains(WEBENGINE_CONFIG, no_ui_delegates): SUBDIRS += webengine/ui
-
+!contains(WEBENGINE_CONFIG, no_ui_delegates) {
+    SUBDIRS += webengine/ui \
+               webengine/ui2
+}
 qtHaveModule(widgets) {
     SUBDIRS += webenginewidgets
     plugins.depends = webenginewidgets
diff --git a/src/webengine/api/qquickwebengineview.cpp b/src/webengine/api/qquickwebengineview.cpp
index 02484548b..f49ac155e 100644
--- a/src/webengine/api/qquickwebengineview.cpp
+++ b/src/webengine/api/qquickwebengineview.cpp
@@ -145,8 +145,7 @@ QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate()
     , m_dpiScale(1.0)
     , m_backgroundColor(Qt::white)
     , m_defaultZoomFactor(1.0)
-    // QTBUG-53467
-    , m_menuEnabled(true)
+    , m_ui2Enabled(false)
 {
     // The gold standard for mobile web content is 160 dpi, and the devicePixelRatio expected
     // is the (possibly quantized) ratio of device dpi to 160 dpi.
@@ -164,8 +163,26 @@ QQuickWebEngineViewPrivate::QQuickWebEngineViewPrivate()
         // 1x, 2x, 3x etc assets that fit an integral number of pixels.
         setDevicePixelRatio(qMax(1, qRound(webPixelRatio)));
     }
+
     if (platform == QLatin1Literal("eglfs"))
-        m_menuEnabled = false;
+        m_ui2Enabled = true;
+
+    const QByteArray dialogSet = qgetenv("QTWEBENGINE_DIALOG_SET");
+
+    if (!dialogSet.isEmpty()) {
+        if (dialogSet == QByteArrayLiteral("QtQuickControls2")) {
+            m_ui2Enabled = true;
+        } else if (dialogSet == QByteArrayLiteral("QtQuickControls1")
+                   && m_ui2Enabled) {
+            m_ui2Enabled = false;
+            qWarning("QTWEBENGINE_DIALOG_SET=QtQuickControls1 forces use of Qt Quick Controls 1 "
+                     "on an eglfs backend. This can crash your application!");
+        } else {
+            qWarning("Ignoring QTWEBENGINE_DIALOG_SET environment variable set to %s. Accepted "
+                     "values are \"QtQuickControls1\" and \"QtQuickControls2\"", dialogSet.data());
+        }
+    }
+
 #ifndef QT_NO_ACCESSIBILITY
     QAccessible::installFactory(&webAccessibleFactory);
 #endif // QT_NO_ACCESSIBILITY
@@ -189,7 +206,7 @@ UIDelegatesManager *QQuickWebEngineViewPrivate::ui()
 {
     Q_Q(QQuickWebEngineView);
     if (m_uIDelegatesManager.isNull())
-        m_uIDelegatesManager.reset(new UIDelegatesManager(q));
+        m_uIDelegatesManager.reset(m_ui2Enabled ? new UI2DelegatesManager(q) : new UIDelegatesManager(q));
     return m_uIDelegatesManager.data();
 }
 
@@ -216,12 +233,6 @@ bool QQuickWebEngineViewPrivate::contextMenuRequested(const WebEngineContextMenu
 {
     Q_Q(QQuickWebEngineView);
 
-    if (!m_menuEnabled) {
-        qWarning("You are trying to open context menu on eglfs backend, which is not currently supported\n"
-                 "See QTBUG-53467.");
-        return false;
-    }
-
     // Assign the WebEngineView as the parent of the menu, so mouse events are properly propagated
     // on OSX.
     QObject *menu = ui()->addMenu(q, QString(), data.pos);
@@ -354,7 +365,7 @@ bool QQuickWebEngineViewPrivate::contextMenuRequested(const WebEngineContextMenu
     }
 
     // Now fire the popup() method on the top level menu
-    QMetaObject::invokeMethod(menu, "popup");
+    ui()->showMenu(menu);
     return true;
 }
 
diff --git a/src/webengine/api/qquickwebengineview_p_p.h b/src/webengine/api/qquickwebengineview_p_p.h
index 98efb822c..7e5e07b95 100644
--- a/src/webengine/api/qquickwebengineview_p_p.h
+++ b/src/webengine/api/qquickwebengineview_p_p.h
@@ -239,8 +239,7 @@ private:
     qreal m_dpiScale;
     QColor m_backgroundColor;
     qreal m_defaultZoomFactor;
-    // QTBUG-53467
-    bool m_menuEnabled;
+    bool m_ui2Enabled;
 };
 
 #ifndef QT_NO_ACCESSIBILITY
diff --git a/src/webengine/ui/PromptDialog.qml b/src/webengine/ui/PromptDialog.qml
index 657bf7631..d9fc61cf8 100644
--- a/src/webengine/ui/PromptDialog.qml
+++ b/src/webengine/ui/PromptDialog.qml
@@ -53,6 +53,10 @@ ApplicationWindow {
     height: 100
     flags: Qt.Dialog
 
+    onClosing: {
+        rejected();
+    }
+
     function open() {
         show();
     }
diff --git a/src/webengine/ui2/AlertDialog.qml b/src/webengine/ui2/AlertDialog.qml
new file mode 100644
index 000000000..4f63c5b70
--- /dev/null
+++ b/src/webengine/ui2/AlertDialog.qml
@@ -0,0 +1,98 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.0
+
+Dialog {
+    property alias text: message.text
+    property bool handled: false
+    signal accepted()
+    signal rejected()
+    title: qsTr("Alert Dialog")
+    modality: Qt.NonModal
+
+    //handle the case where users simply closes the dialog
+    onVisibilityChanged: {
+        if (visible == false && handled == false) {
+            handled = true;
+            rejected();
+        } else {
+            handled = false;
+        }
+    }
+
+    function acceptDialog() {
+        accepted();
+        handled = true;
+        close();
+    }
+
+    contentItem: ColumnLayout {
+        id: rootLayout
+        anchors.fill: parent
+        anchors.margins: 4
+        property int minimumWidth: rootLayout.implicitWidth + rootLayout.doubleMargins
+        property int minimumHeight: rootLayout.implicitHeight + rootLayout.doubleMargins
+        property int doubleMargins: anchors.margins * 2
+        SystemPalette { id: palette; colorGroup: SystemPalette.Active }
+        RowLayout {
+            Layout.alignment: Qt.AlignRight
+            spacing: 8
+            Image {
+                source: "information.png"
+            }
+            Text {
+                id: message
+                Layout.fillWidth: true
+                color: palette.windowText
+            }
+        }
+        Item {
+            Layout.fillHeight: true
+        }
+        Controls.Button {
+            Layout.alignment: Qt.AlignHCenter
+            text: qsTr("OK")
+            onClicked: acceptDialog()
+        }
+    }
+}
diff --git a/src/webengine/ui2/AuthenticationDialog.qml b/src/webengine/ui2/AuthenticationDialog.qml
new file mode 100644
index 000000000..52fcce3f1
--- /dev/null
+++ b/src/webengine/ui2/AuthenticationDialog.qml
@@ -0,0 +1,135 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.0
+
+Dialog {
+    property alias text: message.text
+    property bool handled: false
+    signal accepted(string user, string password)
+    signal rejected()
+    title: qsTr("Authentication Required")
+    modality: Qt.NonModal
+
+    //handle the case where users simply closes the dialog
+    onVisibilityChanged: {
+        if (visible == false && handled == false) {
+            handled = true;
+            rejected();
+        } else {
+            handled = false;
+        }
+    }
+
+    function acceptDialog() {
+        accepted(userField.text, passwordField.text);
+        handled = true;
+        close();
+    }
+
+    function rejectDialog() {
+        rejected();
+        handled = true;
+        close();
+    }
+
+    contentItem: ColumnLayout {
+        id: rootLayout
+        anchors.fill: parent
+        anchors.margins: 4
+        property int minimumWidth: rootLayout.implicitWidth + rootLayout.doubleMargins
+        property int minimumHeight: rootLayout.implicitHeight + rootLayout.doubleMargins
+
+        property int doubleMargins: anchors.margins * 2
+
+        SystemPalette { id: palette; colorGroup: SystemPalette.Active }
+        Text {
+            id: message
+            color: palette.windowText
+        }
+        GridLayout {
+            columns: 2
+            Controls.Label {
+                text: qsTr("Username:")
+                color: palette.windowText
+            }
+            Controls.TextField {
+                id: userField
+                focus: true
+                Layout.fillWidth: true
+                onAccepted: {
+                    if (userField.text && passwordField.text)
+                        acceptDialog();
+                }
+            }
+            Controls.Label {
+                text: qsTr("Password:")
+                color: palette.windowText
+            }
+            Controls.TextField {
+                id: passwordField
+                Layout.fillWidth: true
+                echoMode: TextInput.Password
+                onAccepted: {
+                    if (userField.text && passwordField.text)
+                        acceptDialog();
+                }
+            }
+        }
+        Item {
+            Layout.fillHeight: true
+        }
+        RowLayout {
+            Layout.alignment: Qt.AlignRight
+            spacing: 8
+            Controls.Button {
+                id: cancelButton
+                text: qsTr("Cancel")
+                onClicked: rejectDialog()
+            }
+            Controls.Button {
+                text: qsTr("Log In")
+                onClicked: acceptDialog()
+            }
+        }
+    }
+}
diff --git a/src/webengine/ui2/ConfirmDialog.qml b/src/webengine/ui2/ConfirmDialog.qml
new file mode 100644
index 000000000..0649d3654
--- /dev/null
+++ b/src/webengine/ui2/ConfirmDialog.qml
@@ -0,0 +1,111 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+import QtQuick.Dialogs 1.2
+import QtQuick.Layouts 1.0
+
+Dialog {
+    property alias text: message.text
+    property bool handled: false
+    signal accepted()
+    signal rejected()
+    title: qsTr("Alert Dialog")
+    modality: Qt.NonModal
+
+    //handle the case where users simply closes the dialog
+    onVisibilityChanged: {
+        if (visible == false && handled == false) {
+            handled = true;
+            rejected();
+        } else {
+            handled = false;
+        }
+    }
+
+    function acceptDialog() {
+        accepted();
+        handled = true;
+        close();
+    }
+
+    function rejectDialog() {
+        rejected();
+        handled = true;
+        close();
+    }
+
+    contentItem: ColumnLayout {
+        id: rootLayout
+        anchors.fill: parent
+        anchors.margins: 4
+        property int minimumWidth: rootLayout.implicitWidth + rootLayout.doubleMargins
+        property int minimumHeight: rootLayout.implicitHeight + rootLayout.doubleMargins
+        property int doubleMargins: anchors.margins * 2
+        SystemPalette { id: palette; colorGroup: SystemPalette.Active }
+        RowLayout {
+            Layout.alignment: Qt.AlignRight
+            spacing: 8
+            Image {
+                source: "question.png"
+            }
+            Text {
+                id: message
+                Layout.fillWidth: true
+                color: palette.windowText
+            }
+        }
+        Item {
+            Layout.fillHeight: true
+        }
+        RowLayout {
+            Layout.alignment: Qt.AlignRight
+            spacing: 8
+            Controls.Button {
+                text: qsTr("OK")
+                onClicked: acceptDialog()
+            }
+            Controls.Button {
+                text: qsTr("Cancel")
+                onClicked: rejectDialog()
+            }
+        }
+    }
+}
diff --git a/src/webengine/ui2/Menu.qml b/src/webengine/ui2/Menu.qml
new file mode 100644
index 000000000..0e7b869f5
--- /dev/null
+++ b/src/webengine/ui2/Menu.qml
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+
+Controls.Menu {
+    id: menu
+    signal done()
+
+    // Use private API for now
+    onAboutToHide: doneTimer.start()
+
+    // WORKAROUND On Mac the Menu may be destroyed before the MenuItem
+    // is actually triggered (see qtbase commit 08cc9b9991ae9ab51)
+    Timer {
+        id: doneTimer
+        interval: 100
+        onTriggered: menu.done()
+    }
+}
diff --git a/src/webengine/ui2/MenuItem.qml b/src/webengine/ui2/MenuItem.qml
new file mode 100644
index 000000000..9bf8aac5e
--- /dev/null
+++ b/src/webengine/ui2/MenuItem.qml
@@ -0,0 +1,44 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+
+Controls.MenuItem { }
+
diff --git a/src/webengine/ui2/MenuSeparator.qml b/src/webengine/ui2/MenuSeparator.qml
new file mode 100644
index 000000000..0c664084a
--- /dev/null
+++ b/src/webengine/ui2/MenuSeparator.qml
@@ -0,0 +1,42 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+
+Item { id: dummy }
diff --git a/src/webengine/ui2/PromptDialog.qml b/src/webengine/ui2/PromptDialog.qml
new file mode 100644
index 000000000..880213e36
--- /dev/null
+++ b/src/webengine/ui2/PromptDialog.qml
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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$
+**
+****************************************************************************/
+
+import QtQuick 2.5
+import QtQuick.Controls 2.0 as Controls
+import QtQuick.Layouts 1.0
+import QtQuick.Dialogs 1.2
+
+Dialog {
+    property alias text: message.text
+    property alias prompt: field.text
+    property bool handled: false
+    signal input(string text)
+    signal accepted()
+    signal rejected()
+    title: qsTr("Prompt Dialog")
+    modality: Qt.NonModal
+
+    //handle the case where users simply closes the dialog
+    onVisibilityChanged: {
+        if (visible == false && handled == false) {
+            handled = true;
+            rejected();
+        } else {
+            handled = false;
+        }
+    }
+
+    function acceptDialog() {
+        input(field.text);
+        accepted();
+        handled = true;
+        close();
+    }
+
+    function rejectDialog() {
+        rejected();
+        handled = true;
+        close();
+    }
+
+    contentItem: ColumnLayout {
+        id: rootLayout
+        anchors.fill: parent
+        anchors.margins: 4
+        property int minimumWidth: rootLayout.implicitWidth + rootLayout.doubleMargins
+        property int minimumHeight: rootLayout.implicitHeight + rootLayout.doubleMargins
+        property int doubleMargins: anchors.margins * 2
+        SystemPalette { id: palette; colorGroup: SystemPalette.Active }
+        Text {
+            id: message
+            Layout.fillWidth: true
+            color: palette.windowText
+        }
+        Controls.TextField {
+            id:field
+            focus: true
+            Layout.fillWidth: true
+            onAccepted: acceptDialog()
+        }
+        Item {
+            Layout.fillHeight: true
+        }
+        RowLayout {
+            Layout.alignment: Qt.AlignRight
+            spacing: 8
+            Controls.Button {
+                text: qsTr("OK")
+                onClicked: acceptDialog()
+            }
+            Controls.Button {
+                text: qsTr("Cancel")
+                onClicked: rejectDialog()
+            }
+        }
+    }
+
+}
diff --git a/src/webengine/ui2/information.png b/src/webengine/ui2/information.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a2eb87d108d2a24b71559998627570a252ebe69
GIT binary patch
literal 254
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*VGbSAd`XN|IC>)|NsBr
zJM;84Adj&m$S;_|;n|HeAZM<pi(`n!`Lz=-@*Y;;adC`Wpy88H+7NP`)#FC}vZNdX
z*S~LS8!D`<&snkvGFmiqzmIy&TOWQ^A*6kc*B^2F+mUw;oGG$xOKdQ*&hXq`;e9bt
zH27&qL1aH?i^hR?hT6u*>@%7(T)h=;CEVc^^89x2qI37Z>}e&N%A1yYd9L#M6#8K5
xk^WS{6HL=W1DRjHdJ-(nQORD&&S<T`D!=um?`Nj!X&@gmc)I$ztaD0e0stslUt$0N

literal 0
HcmV?d00001

diff --git a/src/webengine/ui2/qmldir b/src/webengine/ui2/qmldir
new file mode 100644
index 000000000..8ab0d3671
--- /dev/null
+++ b/src/webengine/ui2/qmldir
@@ -0,0 +1,2 @@
+# Internal module
+module QtWebEngine/Controls2Delegates
diff --git a/src/webengine/ui2/question.png b/src/webengine/ui2/question.png
new file mode 100644
index 0000000000000000000000000000000000000000..2dd92fd7915a09de670b8b6022ddcf02d4cc90e1
GIT binary patch
literal 257
zcmeAS@N?(olHy`uVBq!ia0vp^3LwnF3?v&v(vJfv{s5m4*VGbSAd`XN|IC>)|NsBr
zJM;84Adj&m$S;_|;n|HeAZLN6i(`n!`PPZ2d6^Y?T+&^c9hWh=FuH8!-7xcC_9@2C
zoYQ|iTmJmuAqEDPNusMOynhvZeP!x#L1X?d^$hP5mfn}LIr`_t#Jp@W5uIO<AZO{g
zTGnQ1x{ra<6$?kp#(#{y0(Csn2jZt*G?&iaz;xX#X$`|AG0y}IZ-zzRC;nTlsOG7=
zF8G72s`7ScgOl6jI6Sw_=iA}eP}208<pHM?d;C68KkuD$ds9IE@O1TaS?83{1ONzn
BT+ILg

literal 0
HcmV?d00001

diff --git a/src/webengine/ui2/ui2.pro b/src/webengine/ui2/ui2.pro
new file mode 100644
index 000000000..612999929
--- /dev/null
+++ b/src/webengine/ui2/ui2.pro
@@ -0,0 +1,17 @@
+TARGETPATH = QtWebEngine/Controls2Delegates
+
+QML_FILES += \
+    # Authentication Dialog
+    AuthenticationDialog.qml \
+    # JS Dialogs
+    AlertDialog.qml \
+    ConfirmDialog.qml \
+    PromptDialog.qml \
+    # Menus. Based on Qt Quick Controls
+    Menu.qml \
+    MenuItem.qml \
+    MenuSeparator.qml \
+    information.png \
+    question.png
+
+load(qml_module)
diff --git a/src/webengine/ui_delegates_manager.cpp b/src/webengine/ui_delegates_manager.cpp
index 769a30016..608ce2ab3 100644
--- a/src/webengine/ui_delegates_manager.cpp
+++ b/src/webengine/ui_delegates_manager.cpp
@@ -81,22 +81,6 @@ static QString fileNameForComponent(UIDelegatesManager::ComponentType type)
     return QString();
 }
 
-static QString getUIDelegatesImportDir(QQmlEngine *engine) {
-    static QString importDir;
-    static bool initialized = false;
-    if (initialized)
-        return importDir;
-    Q_FOREACH (const QString &path, engine->importPathList()) {
-        QFileInfo fi(path % QLatin1String("/QtWebEngine/UIDelegates/"));
-        if (fi.exists()) {
-            importDir = fi.absolutePath();
-            break;
-        }
-    }
-    initialized = true;
-    return importDir;
-}
-
 static QPoint calculateToolTipPosition(QPoint &position, QSize &toolTip) {
     QRect screen;
     QList<QScreen *> screens = QGuiApplication::screens();
@@ -150,16 +134,32 @@ UIDelegatesManager::UIDelegatesManager(QQuickWebEngineView *view)
 {
 }
 
+UIDelegatesManager::~UIDelegatesManager()
+{
+}
+
 #define COMPONENT_MEMBER_CASE_STATEMENT(TYPE, COMPONENT) \
     case TYPE: \
         component = &COMPONENT##Component; \
         break;
 
+bool UIDelegatesManager::initializeImportDirs(QStringList &dirs, QQmlEngine *engine) {
+    foreach (const QString &path, engine->importPathList()) {
+        QFileInfo fi(path % QLatin1String("/QtWebEngine/UIDelegates/"));
+        if (fi.exists()) {
+            dirs << fi.absolutePath();
+            return true;
+        }
+    }
+    return false;
+}
+
 bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
 {
     QQmlEngine* engine = qmlEngine(m_view);
-    if (getUIDelegatesImportDir(engine).isNull())
+    if (m_importDirs.isEmpty() && !initializeImportDirs(m_importDirs, engine))
         return false;
+
     QQmlComponent **component;
     switch (type) {
     FOR_EACH_COMPONENT_TYPE(COMPONENT_MEMBER_CASE_STATEMENT, NO_SEPARATOR)
@@ -176,20 +176,25 @@ bool UIDelegatesManager::ensureComponentLoaded(ComponentType type)
 #endif
     if (!engine)
         return false;
-    QFileInfo fi(getUIDelegatesImportDir(engine) % QLatin1Char('/') % fileName);
-    if (!fi.exists())
-        return false;
-    // FIXME: handle async loading
-    *component = (new QQmlComponent(engine, QUrl::fromLocalFile(fi.absoluteFilePath()), QQmlComponent::PreferSynchronous, m_view));
-
-    if ((*component)->status() != QQmlComponent::Ready) {
-        Q_FOREACH (const QQmlError& err, (*component)->errors())
-            qWarning("QtWebEngine: component error: %s\n", qPrintable(err.toString()));
-        delete *component;
-        *component = 0;
-        return false;
+
+    foreach (const QString &importDir, m_importDirs) {
+        QFileInfo fi(importDir % QLatin1Char('/') % fileName);
+        if (!fi.exists())
+            continue;
+        // FIXME: handle async loading
+        *component = (new QQmlComponent(engine, QUrl::fromLocalFile(fi.absoluteFilePath()),
+                                        QQmlComponent::PreferSynchronous, m_view));
+
+        if ((*component)->status() != QQmlComponent::Ready) {
+            foreach (const QQmlError &err, (*component)->errors())
+                qWarning("QtWebEngine: component error: %s\n", qPrintable(err.toString()));
+            delete *component;
+            *component = nullptr;
+            return false;
+        }
+        return true;
     }
-    return true;
+    return false;
 }
 
 #define CHECK_QML_SIGNAL_PROPERTY(prop, location) \
@@ -241,11 +246,11 @@ QObject *UIDelegatesManager::addMenu(QObject *parentMenu, const QString &title,
 {
     Q_ASSERT(parentMenu);
     if (!ensureComponentLoaded(Menu))
-        return 0;
+        return nullptr;
     QQmlContext *context = qmlContext(m_view);
     QObject *menu = menuComponent->beginCreate(context);
     // set visual parent for non-Window-based menus
-    if (QQuickItem* item = qobject_cast<QQuickItem*>(menu))
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(menu))
         item->setParentItem(m_view);
 
     if (!title.isEmpty())
@@ -321,7 +326,7 @@ void UIDelegatesManager::showDialog(QSharedPointer<JavaScriptDialogController> d
     QQmlContext *context = qmlContext(m_view);
     QObject *dialog = dialogComponent->beginCreate(context);
     // set visual parent for non-Window-based dialogs
-    if (QQuickItem* item = qobject_cast<QQuickItem*>(dialog))
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(dialog))
         item->setParentItem(m_view);
     dialog->setParent(m_view);
     QQmlProperty textProp(dialog, QStringLiteral("text"));
@@ -347,8 +352,6 @@ void UIDelegatesManager::showDialog(QSharedPointer<JavaScriptDialogController> d
         CHECK_QML_SIGNAL_PROPERTY(inputSignal, dialogComponent->url());
         static int setTextIndex = dialogController->metaObject()->indexOfSlot("textProvided(QString)");
         QObject::connect(dialog, inputSignal.method(), dialogController.data(), dialogController->metaObject()->method(setTextIndex));
-        QQmlProperty closingSignal(dialog, QStringLiteral("onClosing"));
-        QObject::connect(dialog, closingSignal.method(), dialogController.data(), dialogController->metaObject()->method(rejectIndex));
     }
 
     dialogComponent->completeCreate();
@@ -369,7 +372,7 @@ void UIDelegatesManager::showColorDialog(QSharedPointer<ColorChooserController>
 
     QQmlContext *context = qmlContext(m_view);
     QObject *colorDialog = colorDialogComponent->beginCreate(context);
-    if (QQuickItem* item = qobject_cast<QQuickItem*>(colorDialog))
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(colorDialog))
         item->setParentItem(m_view);
     colorDialog->setParent(m_view);
 
@@ -409,7 +412,7 @@ void UIDelegatesManager::showDialog(QSharedPointer<AuthenticationDialogControlle
     QQmlContext *context = qmlContext(m_view);
     QObject *authenticationDialog = authenticationDialogComponent->beginCreate(context);
     // set visual parent for non-Window-based dialogs
-    if (QQuickItem* item = qobject_cast<QQuickItem*>(authenticationDialog))
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(authenticationDialog))
         item->setParentItem(m_view);
     authenticationDialog->setParent(m_view);
 
@@ -450,7 +453,7 @@ void UIDelegatesManager::showFilePicker(FilePickerController *controller)
 
     QQmlContext *context = qmlContext(m_view);
     QObject *filePicker = filePickerComponent->beginCreate(context);
-    if (QQuickItem* item = qobject_cast<QQuickItem*>(filePicker))
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(filePicker))
         item->setParentItem(m_view);
     filePicker->setParent(m_view);
     filePickerComponent->completeCreate();
@@ -491,6 +494,11 @@ void UIDelegatesManager::showFilePicker(FilePickerController *controller)
     QMetaObject::invokeMethod(filePicker, "open");
 }
 
+void UIDelegatesManager::showMenu(QObject *menu)
+{
+     QMetaObject::invokeMethod(menu, "popup");
+}
+
 void UIDelegatesManager::showMessageBubble(const QRect &anchor, const QString &mainText, const QString &subText)
 {
     if (!ensureComponentLoaded(MessageBubble))
@@ -557,4 +565,84 @@ void UIDelegatesManager::showToolTip(const QString &text)
     QMetaObject::invokeMethod(m_toolTip.data(), "open");
 }
 
+UI2DelegatesManager::UI2DelegatesManager(QQuickWebEngineView *view) : UIDelegatesManager(view)
+{
+
+}
+
+bool UI2DelegatesManager::initializeImportDirs(QStringList &dirs, QQmlEngine *engine)
+{
+    foreach (const QString &path, engine->importPathList()) {
+        QFileInfo fi1(path % QLatin1String("/QtWebEngine/Controls2Delegates/"));
+        QFileInfo fi2(path % QLatin1String("/QtWebEngine/UIDelegates/"));
+        if (fi1.exists() && fi2.exists()) {
+            dirs << fi1.absolutePath() << fi2.absolutePath();
+            return true;
+        }
+    }
+    return false;
+}
+
+QObject *UI2DelegatesManager::addMenu(QObject *parentMenu, const QString &title, const QPoint &pos)
+{
+    Q_ASSERT(parentMenu);
+    if (!ensureComponentLoaded(Menu))
+        return nullptr;
+    QQmlContext *context = qmlContext(m_view);
+    QObject *menu = menuComponent->beginCreate(context);
+
+    // set visual parent for non-Window-based menus
+    if (QQuickItem *item = qobject_cast<QQuickItem*>(menu))
+        item->setParentItem(m_view);
+
+    if (!title.isEmpty())
+        menu->setProperty("title", title);
+    if (!pos.isNull()) {
+        menu->setProperty("x", pos.x());
+        menu->setProperty("y", pos.y());
+    }
+
+    menu->setParent(parentMenu);
+    QQmlProperty doneSignal(menu, QStringLiteral("onDone"));
+    CHECK_QML_SIGNAL_PROPERTY(doneSignal, menuComponent->url())
+    static int deleteLaterIndex = menu->metaObject()->indexOfSlot("deleteLater()");
+    QObject::connect(menu, doneSignal.method(), menu, menu->metaObject()->method(deleteLaterIndex));
+    menuComponent->completeCreate();
+    return menu;
+}
+
+void UI2DelegatesManager::addMenuItem(MenuItemHandler *menuItemHandler, const QString &text,
+                                      const QString &/*iconName*/, bool enabled,
+                                      bool checkable, bool checked)
+{
+    Q_ASSERT(menuItemHandler);
+    if (!ensureComponentLoaded(MenuItem))
+        return;
+
+    QObject *it = menuItemComponent->beginCreate(qmlContext(m_view));
+
+    it->setProperty("text", text);
+    it->setProperty("enabled", enabled);
+    it->setProperty("checked", checked);
+    it->setProperty("checkable", checkable);
+
+    QQmlProperty signal(it, QStringLiteral("onTriggered"));
+    CHECK_QML_SIGNAL_PROPERTY(signal, menuItemComponent->url());
+    QObject::connect(it, signal.method(), menuItemHandler,
+                     QMetaMethod::fromSignal(&MenuItemHandler::triggered));
+    menuItemComponent->completeCreate();
+
+    QObject *menu = menuItemHandler->parent();
+    it->setParent(menu);
+
+    QQmlListReference entries(menu, defaultPropertyName(menu), qmlEngine(m_view));
+    if (entries.isValid())
+        entries.append(it);
+}
+
+void UI2DelegatesManager::showMenu(QObject *menu)
+{
+    QMetaObject::invokeMethod(menu, "open");
+}
+
 } // namespace QtWebEngineCore
diff --git a/src/webengine/ui_delegates_manager.h b/src/webengine/ui_delegates_manager.h
index 43d1e6985..b1eb65513 100644
--- a/src/webengine/ui_delegates_manager.h
+++ b/src/webengine/ui_delegates_manager.h
@@ -74,6 +74,7 @@ class QQmlContext;
 class QQmlComponent;
 class QQuickItem;
 class QQuickWebEngineView;
+class QQmlEngine;
 QT_END_NAMESPACE
 
 namespace QtWebEngineCore {
@@ -103,27 +104,35 @@ public:
     };
 
     UIDelegatesManager(QQuickWebEngineView *);
+    virtual ~UIDelegatesManager();
 
-    void addMenuItem(MenuItemHandler *menuItemHandler, const QString &text, const QString &iconName = QString(),
-                     bool enabled = true, bool checkable = false, bool checked = true);
+    virtual bool initializeImportDirs(QStringList &dirs, QQmlEngine *engine);
+    virtual void addMenuItem(MenuItemHandler *menuItemHandler, const QString &text,
+                             const QString &iconName = QString(),
+                             bool enabled = true,
+                             bool checkable = false, bool checked = true);
     void addMenuSeparator(QObject *menu);
-    QObject *addMenu(QObject *parentMenu, const QString &title, const QPoint &pos = QPoint());
+    virtual QObject *addMenu(QObject *parentMenu, const QString &title,
+                             const QPoint &pos = QPoint());
     QQmlContext *creationContextForComponent(QQmlComponent *);
     void showColorDialog(QSharedPointer<ColorChooserController>);
     void showDialog(QSharedPointer<JavaScriptDialogController>);
     void showDialog(QSharedPointer<AuthenticationDialogController>);
     void showFilePicker(FilePickerController *controller);
-    void showMessageBubble(const QRect &anchor, const QString &mainText, const QString &subText);
+    virtual void showMenu(QObject *menu);
+    void showMessageBubble(const QRect &anchor, const QString &mainText,
+                           const QString &subText);
     void hideMessageBubble();
     void moveMessageBubble(const QRect &anchor);
     void showToolTip(const QString &text);
 
-private:
+protected:
     bool ensureComponentLoaded(ComponentType);
 
     QQuickWebEngineView *m_view;
     QScopedPointer<QQuickItem> m_messageBubbleItem;
     QScopedPointer<QObject> m_toolTip;
+    QStringList m_importDirs;
 
     FOR_EACH_COMPONENT_TYPE(MEMBER_DECLARATION, SEMICOLON_SEPARATOR)
 
@@ -131,6 +140,24 @@ private:
 
 };
 
+// delegate manager for qtquickcontrols2 with fallback to qtquickcontrols1
+
+class UI2DelegatesManager : public UIDelegatesManager
+{
+public:
+    UI2DelegatesManager(QQuickWebEngineView *);
+    bool initializeImportDirs(QStringList &dirs, QQmlEngine *engine) override;
+    QObject *addMenu(QObject *parentMenu, const QString &title,
+                     const QPoint &pos = QPoint()) override;
+    void addMenuItem(MenuItemHandler *menuItemHandler, const QString &text,
+                     const QString &iconName = QString(),
+                     bool enabled = true,
+                     bool checkable = false, bool checked = false) override;
+    void showMenu(QObject *menu) override;
+    Q_DISABLE_COPY(UI2DelegatesManager)
+
+};
+
 } // namespace QtWebEngineCore
 
 #endif // UI_DELEGATES_MANAGER_H
diff --git a/sync.profile b/sync.profile
index e4a88e0c2..e713faee4 100644
--- a/sync.profile
+++ b/sync.profile
@@ -24,5 +24,6 @@
     "qttools" => "",
 # FIXME: take examples out into their own module to avoid a potential circular dependency later ?
     "qtquickcontrols" => "",
+    "qtquickcontrols2" => "",
     "qtwebchannel" => "",
 );
-- 
GitLab