diff options
Diffstat (limited to 'tests/manual')
95 files changed, 3124 insertions, 427 deletions
diff --git a/tests/manual/CMakeLists.txt b/tests/manual/CMakeLists.txt index c6d3e6e50..d9f3e11ea 100644 --- a/tests/manual/CMakeLists.txt +++ b/tests/manual/CMakeLists.txt @@ -1,2 +1,7 @@ -add_subdirectory(quick) -add_subdirectory(widgets) +add_subdirectory(examples) +if(TARGET Qt::WebEngineQuick) + add_subdirectory(quick) +endif() +if(TARGET Qt::WebEngineWidgets) + add_subdirectory(widgets) +endif() diff --git a/tests/manual/examples/CMakeLists.txt b/tests/manual/examples/CMakeLists.txt new file mode 100644 index 000000000..c56302f20 --- /dev/null +++ b/tests/manual/examples/CMakeLists.txt @@ -0,0 +1,6 @@ +if(TARGET Qt::WebEngineWidgets) + add_subdirectory(widgets) +endif() +if(TARGET Qt::WebEngineQuick) + add_subdirectory(quick) +endif() diff --git a/tests/manual/examples/quick/CMakeLists.txt b/tests/manual/examples/quick/CMakeLists.txt new file mode 100644 index 000000000..c461f2dbb --- /dev/null +++ b/tests/manual/examples/quick/CMakeLists.txt @@ -0,0 +1,4 @@ +add_subdirectory(minimal) +add_subdirectory(customdialogs) +add_subdirectory(customtouchhandle) +add_subdirectory(webengineaction) diff --git a/tests/manual/examples/quick/customdialogs/CMakeLists.txt b/tests/manual/examples/quick/customdialogs/CMakeLists.txt new file mode 100644 index 000000000..bfbff79eb --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/CMakeLists.txt @@ -0,0 +1,56 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(customdialogs LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(customdialogs + SOURCES main.cpp server.cpp server.h +) + +set_target_properties(customdialogs PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(customdialogs PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +set(customdialogs_resource_files + "MessageRectangle.qml" + "SwitchButton.qml" + "WebView.qml" + "forms/Authentication.qml" + "forms/AuthenticationForm.ui.qml" + "forms/ColorCell.qml" + "forms/ColorPicker.qml" + "forms/ColorPickerForm.ui.qml" + "forms/CustomButton.qml" + "forms/FilePicker.qml" + "forms/FilePickerForm.ui.qml" + "forms/FileRow.qml" + "forms/JavaScript.qml" + "forms/JavaScriptForm.ui.qml" + "forms/Menu.qml" + "forms/MenuForm.ui.qml" + "forms/TouchSelectionMenu.qml" + "forms/TouchSelectionMenuForm.ui.qml" + "icon.svg" + "index.html" + "main.qml" + "style.css" +) + +qt_add_resources(customdialogs "customdialogs" + PREFIX + "/" + FILES + ${customdialogs_resource_files} +) + diff --git a/tests/manual/examples/quick/customdialogs/MessageRectangle.qml b/tests/manual/examples/quick/customdialogs/MessageRectangle.qml new file mode 100644 index 000000000..09a202cf3 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/MessageRectangle.qml @@ -0,0 +1,18 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + property alias text: messageText.text + width: parent.width + height: 30 + visible: false + color: "#80c342" + Text { + id: messageText + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.pointSize: 12 + } +} diff --git a/tests/manual/examples/quick/customdialogs/SwitchButton.qml b/tests/manual/examples/quick/customdialogs/SwitchButton.qml new file mode 100644 index 000000000..69fc1427e --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/SwitchButton.qml @@ -0,0 +1,23 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + width: parent.width + height: 40 + property alias checked: switcher.checked + RowLayout { + anchors.centerIn: parent + Text { + text: qsTr("Use default dialogs") + font.pointSize: 12 + } + Switch { + id: switcher + checked: true + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/WebView.qml b/tests/manual/examples/quick/customdialogs/WebView.qml new file mode 100644 index 000000000..5c99ee7e7 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/WebView.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +WebEngineView { + id: view + url: "qrc:/index.html" + property bool useDefaultDialogs: true + signal openForm(var form) + + Rectangle { + id: tooltip + width: 200 + height: 30 + z: 50 + visible: false + color: "gray" + border.color: "black" + border.width: 2 + radius: 3 + + property string text: "" + + Text { + x: 0 + y: 0 + color: "#ffffff" + text: parent.text + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.bold: false + } + + } + + onContextMenuRequested: function(request) { + // we only show menu for links with #openMenu + if (!request.linkUrl.toString().endsWith("#openMenu")) { + request.accepted = true; + return; + } + // return early to show default menu + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/Menu.qml"), + properties: {"request": request}}); + } + + onTooltipRequested: function(request) { + if (useDefaultDialogs) + return; + + if (request.type == TooltipRequest.Show) { + tooltip.visible = true; + tooltip.x = request.x; + tooltip.y = request.y; + tooltip.text = request.text; + } else { + tooltip.visible = false; + } + + request.accepted = true; + } + + onAuthenticationDialogRequested: function(request) { + if (useDefaultDialogs) { + // do not show proxy error page + view.url = "qrc:/index.html" + return; + } + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/Authentication.qml"), + properties: {"request": request}}); + } + + onJavaScriptDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/JavaScript.qml"), + properties: {"request": request}}); + } + + onColorDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/ColorPicker.qml"), + properties: {"request": request}}); + } + + onFileDialogRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/FilePicker.qml"), + properties: {"request": request}}); + + } + + onTouchSelectionMenuRequested: function(request) { + if (useDefaultDialogs) + return; + + request.accepted = true; + openForm({item: Qt.resolvedUrl("forms/TouchSelectionMenu.qml"), + properties: {"request": request}}); + } +} diff --git a/tests/manual/examples/quick/customdialogs/customdialogs.pro b/tests/manual/examples/quick/customdialogs/customdialogs.pro new file mode 100644 index 000000000..1b9a6778e --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/customdialogs.pro @@ -0,0 +1,18 @@ +QT += webenginequick + +HEADERS += \ + server.h + +SOURCES += \ + main.cpp \ + server.cpp + +RESOURCES += \ + customdialogs.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/customdialogs +INSTALLS += target + +qtHaveModule(widgets) { + QT += widgets # QApplication is required to get native styling with QtQuickControls +} diff --git a/tests/manual/examples/quick/customdialogs/customdialogs.qrc b/tests/manual/examples/quick/customdialogs/customdialogs.qrc new file mode 100644 index 000000000..bb2677198 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/customdialogs.qrc @@ -0,0 +1,24 @@ +<RCC> + <qresource prefix="/"> + <file>forms/AuthenticationForm.ui.qml</file> + <file>forms/Authentication.qml</file> + <file>forms/ColorCell.qml</file> + <file>forms/ColorPickerForm.ui.qml</file> + <file>forms/ColorPicker.qml</file> + <file>forms/CustomButton.qml</file> + <file>forms/FilePickerForm.ui.qml</file> + <file>forms/FilePicker.qml</file> + <file>forms/FileRow.qml</file> + <file>forms/JavaScriptForm.ui.qml</file> + <file>forms/JavaScript.qml</file> + <file>forms/MenuForm.ui.qml</file> + <file>forms/Menu.qml</file> + <file>icon.svg</file> + <file>index.html</file> + <file>main.qml</file> + <file>MessageRectangle.qml</file> + <file>style.css</file> + <file>SwitchButton.qml</file> + <file>WebView.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/examples/quick/customdialogs/forms/Authentication.qml b/tests/manual/examples/quick/customdialogs/forms/Authentication.qml new file mode 100644 index 000000000..151a7c4aa --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/Authentication.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +AuthenticationForm { + property QtObject request + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + loginButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + Component.onCompleted: { + switch (request.type) { + case AuthenticationDialogRequest.AuthenticationTypeHTTP: + console.log("HTTP Authentication Required. Host says: " + request.realm); + break; + case AuthenticationDialogRequest.AuthenticationTypeProxy: + console.log("Proxy Authentication Required for: " + request.proxyHost); + break; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml new file mode 100644 index 000000000..f14986b20 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/AuthenticationForm.ui.qml @@ -0,0 +1,137 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Item { + id: item1 + property alias cancelButton: cancelButton + property alias loginButton: loginButton + property alias userName: userName + property alias password: password + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + GradientStop { + color: "#188bd0" + } + } + + Text { + id: textArea + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Restricted Area") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + Text { + id: userNameText + text: qsTr("Username:") + font.pointSize: 12 + } + + TextField { + id: userName + width: 300 + height: 22 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Text { + id: passwordText + text: qsTr("Password:") + font.pointSize: 12 + } + + TextField { + id: password + width: 300 + height: 26 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + echoMode: TextInput.Password + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: loginButton + width: 90 + height: 30 + btnText: qsTr("Login") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml b/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml new file mode 100644 index 000000000..57151780c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorCell.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: rectangle + width: 50 + height: 50 + signal clicked() + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: rectangle.clicked() + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml b/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml new file mode 100644 index 000000000..63269ddff --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorPicker.qml @@ -0,0 +1,31 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +ColorPickerForm { + property QtObject request + signal closeForm() + + okButton.onClicked: { + request.dialogAccept(colorPicker.color); + closeForm(); + } + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + function createCallback(color) { + return function() { colorPicker.color = color }; + } + + Component.onCompleted:{ + for (var i = 0; i < grid.children.length; i++) { + var cell = grid.children[i]; + cell.clicked.connect(createCallback(cell.color)); + } + colorPicker.color = request.color; + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml new file mode 100644 index 000000000..060aeef7d --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/ColorPickerForm.ui.qml @@ -0,0 +1,186 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias blue1: blue1 + property alias grid: grid + property alias colorPicker: colorPicker + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Select Color") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + GridLayout { + id: grid + columns: 5 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + ColorCell { + id: blue1 + color: "#26d5f8" + } + ColorCell { + id: green1 + color: "#25f93d" + } + ColorCell { + id: red1 + color: "#f71111" + } + ColorCell { + id: yellow1 + color: "#faf23c" + } + ColorCell { + id: orange1 + color: "#ec8505" + } + ColorCell { + id: blue2 + color: "#037eaa" + } + ColorCell { + id: green2 + color: "#389a13" + } + ColorCell { + id: red2 + color: "#b2001b" + } + ColorCell { + id: yellow2 + color: "#caca03" + } + ColorCell { + id: orange2 + color: "#bb4900" + } + ColorCell { + id: blue3 + color: "#01506c" + } + ColorCell { + id: green3 + color: "#37592b" + } + ColorCell { + id: red3 + color: "#700113" + } + ColorCell { + id: yellow3 + color: "#848404" + } + + ColorCell { + id: orange3 + color: "#563100" + } + } + + Item { + width: 10 + height: 10 + } + + Rectangle { + width: 90 + height: 90 + color: "#000000" + radius: 4 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + + Rectangle { + id: colorPicker + height: 80 + color: "#ffffff" + anchors.rightMargin: 5 + anchors.leftMargin: 5 + anchors.bottomMargin: 5 + anchors.topMargin: 5 + anchors.fill: parent + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml b/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml new file mode 100644 index 000000000..00a06d558 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/CustomButton.qml @@ -0,0 +1,61 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +Rectangle { + id: root + width: 200 + height: 30 + radius: 5 + property string btnText: "Name" + property bool btnEnable: true + property bool btnBlue: true + opacity: btnEnable ? 1.0 : 0.5 + signal clicked() + gradient: btnBlue ? blueButton : greenButton + Text { + id: textArea + x: 54 + y: 5 + color: "#ffffff" + text: parent.btnText + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + font.bold: false + } + + MouseArea { + id: mouseArea + anchors.fill: parent + onClicked: { + if (btnEnable) + root.clicked(); + } + } + + Gradient { + id: blueButton + GradientStop { + position: 0 + color: "#25a6e2" + } + GradientStop { + position: mouseArea.pressed && root.btnEnable ? 0.7 :1 + color: "#188bd0" + } + } + + Gradient { + id: greenButton + GradientStop { + position: 0 + color: "#80c342" + } + GradientStop { + position: mouseArea.pressed && root.btnEnable ? 0.7 :1 + color: "#5fac18" + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml b/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml new file mode 100644 index 000000000..45ffefb3a --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FilePicker.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +FilePickerForm { + property QtObject request + property string selectedFile + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + okButton.onClicked: { + request.dialogAccept('/' + selectedFile); + closeForm(); + } + + function createCallback(fileIndex) { + return function() { + for (var i = 0; i < files.children.length; i++) { + var file = files.children[i]; + if (i === fileIndex) { + selectedFile = file.text; + file.selected = true; + } else { + file.selected = false; + } + } + } + } + + Component.onCompleted: { + selectedFile = request.defaultFileName; + for (var i = 0; i < files.children.length; i++) { + var file = files.children[i]; + file.clicked.connect(createCallback(i)); + if (file.text === selectedFile) + file.selected = true; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml new file mode 100644 index 000000000..1e99b1a91 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FilePickerForm.ui.qml @@ -0,0 +1,128 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias files: files + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Select File") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + ColumnLayout { + id: files + + FileRow { + id: filename1 + text: "example.qdoc" + } + + FileRow { + id: filename2 + text: "factory.cpp" + } + + FileRow { + id: filename3 + text: "index.html" + } + + FileRow { + id: filename4 + text: "main.qml" + } + + FileRow { + id: filename5 + text: "qt-logo.png" + } + + FileRow { + id: filename6 + text: "window.h" + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 20 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/FileRow.qml b/tests/manual/examples/quick/customdialogs/forms/FileRow.qml new file mode 100644 index 000000000..1a0cfc0a0 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/FileRow.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + id: root + height: 30 + property string text: "Filename" + property bool selected: false + signal clicked() + + RowLayout { + id: fileRow + width: 100 + + Item { + id: item5 + width: 10 + height: 10 + } + + Rectangle { + id: rectangle2 + width: 10 + height: 10 + color: selected ? "#80c342" : "#25a6e2" + } + + Text { + id: filename + text: root.text + font.pointSize: 12 + } + } + + MouseArea { + id: mouseArea + width: 200 + height: 30 + onClicked: root.clicked() + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml b/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml new file mode 100644 index 000000000..132c95697 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/JavaScript.qml @@ -0,0 +1,44 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtWebEngine + +JavaScriptForm { + property QtObject request + signal closeForm() + + cancelButton.onClicked: { + request.dialogReject(); + closeForm(); + } + + okButton.onClicked: { + request.dialogAccept(prompt.text); + closeForm(); + } + + Component.onCompleted: { + switch (request.type) { + case JavaScriptDialogRequest.DialogTypeAlert: + cancelButton.visible = false; + title = qsTr("Alert"); + message = request.message; + prompt.text = ""; + prompt.visible = false; + break; + case JavaScriptDialogRequest.DialogTypeConfirm: + title = qsTr("Confirm"); + message = request.message; + prompt.text = ""; + prompt.visible = false; + break; + case JavaScriptDialogRequest.DialogTypePrompt: + title = qsTr("Prompt"); + message = request.message; + prompt.text = request.defaultText; + prompt.visible = true; + break; + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml new file mode 100644 index 000000000..b535e7ef9 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/JavaScriptForm.ui.qml @@ -0,0 +1,117 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls + +Item { + id: root + property alias cancelButton: cancelButton + property alias okButton: okButton + property string message: "Message" + property string title: "Title" + property alias prompt: prompt + + ColumnLayout { + id: columnLayout + anchors.topMargin: 20 + anchors.top: parent.top + anchors.bottomMargin: 20 + anchors.bottom: parent.bottom + anchors.rightMargin: 20 + anchors.right: parent.right + anchors.leftMargin: 20 + anchors.left: parent.left + + Image { + id: image + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + Rectangle { + id: rectangle + width: parent.width + height: 30 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + gradient: Gradient { + GradientStop { + position: 0 + color: "#25a6e2" + } + + GradientStop { + color: "#188bd0" + } + } + + Text { + id: title + x: 54 + y: 5 + color: "#ffffff" + text: qsTr("Title") + font.pointSize: 12 + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + } + } + + Item { + width: 40 + height: 40 + } + + Text { + id: message + text: root.message + font.pointSize: 12 + } + + TextField { + id: prompt + width: 300 + height: 22 + Layout.fillWidth: true + font.pointSize: 12 + color: "black" + + background: Rectangle { + color: "white" + border.color: "black" + border.width: 1 + } + } + + Item { + Layout.fillHeight: true + } + + RowLayout { + id: rowLayout + width: 100 + height: 100 + + Item { + Layout.fillWidth: true + } + + CustomButton { + id: cancelButton + width: 90 + height: 30 + btnText: qsTr("Cancel") + btnBlue: false + } + + CustomButton { + id: okButton + width: 90 + height: 30 + btnText: qsTr("OK") + btnBlue: false + } + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/Menu.qml b/tests/manual/examples/quick/customdialogs/forms/Menu.qml new file mode 100644 index 000000000..b90802a0c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/Menu.qml @@ -0,0 +1,22 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +MenuForm { + property QtObject request + signal closeForm() + + followLink.onClicked: closeForm() + back.onClicked: closeForm() + forward.onClicked: closeForm() + reload.onClicked: closeForm() + copyLinkUrl.onClicked: closeForm() + saveLink.onClicked: closeForm() + close.onClicked: closeForm() + + Component.onCompleted: { + back.btnEnable = false; + forward.btnEnable = false; + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml new file mode 100644 index 000000000..b4c06bb7d --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/MenuForm.ui.qml @@ -0,0 +1,65 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias followLink: followLink + property alias back: back + property alias forward: forward + property alias reload: reload + property alias copyLinkUrl: copyLinkUrl + property alias saveLink: saveLink + property alias close: close + + ColumnLayout { + id: columnLayout + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + Image { + id: image + width: 100 + height: 100 + Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter + source: "qrc:/icon.svg" + } + + CustomButton { + id: followLink + btnText: qsTr("Follow") + } + + CustomButton { + id: back + btnText: qsTr("Back") + } + + CustomButton { + id: forward + btnText: qsTr("Forward") + } + + CustomButton { + id: reload + btnText: qsTr("Reload") + } + + CustomButton { + id: copyLinkUrl + btnText: qsTr("Copy Link URL") + } + + CustomButton { + id: saveLink + btnText: qsTr("Save Link") + } + + CustomButton { + id: close + btnBlue: false + btnText: qsTr("Close") + } + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml new file mode 100644 index 000000000..1b0c19789 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenu.qml @@ -0,0 +1,14 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick + +TouchSelectionMenuForm { + property QtObject request + signal closeForm() + + cut.onClicked: closeForm() + copy.onClicked: closeForm() + paste.onClicked: closeForm() + contextMenu.onClicked: closeForm() +} diff --git a/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml new file mode 100644 index 000000000..bed39566f --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/TouchSelectionMenuForm.ui.qml @@ -0,0 +1,39 @@ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Layouts + +Item { + property alias cut: cut + property alias copy: copy + property alias paste: paste + property alias contextMenu: contextMenu + + ColumnLayout { + id: columnLayout + anchors.verticalCenter: parent.verticalCenter + anchors.horizontalCenter: parent.horizontalCenter + + CustomButton { + id: cut + btnText: qsTr("Cut") + } + + CustomButton { + id: copy + btnText: qsTr("Copy") + } + + CustomButton { + id: paste + btnText: qsTr("Paste") + } + + CustomButton { + id: contextMenu + btnText: qsTr("...") + } + + } +} diff --git a/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject b/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject new file mode 100644 index 000000000..b06afaaf1 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/forms/forms.qmlproject @@ -0,0 +1,45 @@ +import QmlProject + +Project { + mainFile: "MenuForm.ui.qml" + + /* Include .qml, .js, and image files from current directory and subdirectories */ + QmlFiles { + directory: "." + } + + JavaScriptFiles { + directory: "." + } + + ImageFiles { + directory: "." + } + + Files { + filter: "*.conf" + files: ["qtquickcontrols2.conf"] + } + + Files { + filter: "qmldir" + directory: "." + } + + Files { + filter: "*.ttf;*.otf" + } + + Environment { + QT_QUICK_CONTROLS_CONF: "qtquickcontrols2.conf" + QT_AUTO_SCREEN_SCALE_FACTOR: "1" + } + + qt6Project: true + + /* List of plugin directories passed to QML runtime */ + importPaths: [ ".", "imports" ] + + /* Required for deployment */ + targetDirectory: "/opt/forms" +} diff --git a/tests/manual/examples/quick/customdialogs/icon.svg b/tests/manual/examples/quick/customdialogs/icon.svg new file mode 100644 index 000000000..48271180b --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/icon.svg @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Generator: Adobe Illustrator 19.2.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) --> +<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" + width="94px" height="94px" viewBox="0 0 94 94" enable-background="new 0 0 94 94" xml:space="preserve"> +<g> + <circle fill="none" cx="47" cy="47" r="47"/> + <g> + <path fill="#46A2DA" d="M47,92.979c-11.779,0-23.559-4.484-32.526-13.451C-3.461,61.591-3.461,32.409,14.472,14.474 + C32.41-3.463,61.592-3.461,79.526,14.473c17.935,17.936,17.935,47.119,0.002,65.054l-0.002,0.001 + C70.559,88.495,58.779,92.979,47,92.979z"/> + </g> + <path fill="#80C342" d="M93,47C93,21.595,72.405,1,47,1C34.297,1,22.797,6.149,14.473,14.473l65.054,65.054 + C87.851,71.203,93,59.703,93,47z"/> + <g> + <path fill="#46A2DA" d="M47,65c-4.808,0-9.328-1.873-12.728-5.272c-7.018-7.019-7.018-18.438,0-25.456 + C37.672,30.873,42.192,29,47,29s9.328,1.873,12.728,5.272c7.018,7.019,7.018,18.438,0,25.456C56.328,63.127,51.808,65,47,65z"/> + <path fill="#FFFFFF" d="M62.248,59.919c6.671-7.858,6.312-19.644-1.105-27.061C57.237,28.953,52.118,27,47,27 + c-5.118,0-10.237,1.953-14.142,5.858c-7.81,7.81-7.81,20.474,0,28.284C36.763,65.047,41.882,67,47,67 + c4.379,0,8.752-1.441,12.372-4.3L77.88,81.209c0.989-0.895,1.935-1.837,2.843-2.814L62.248,59.919z M35.686,58.314 + c-6.238-6.238-6.238-16.389,0-22.627C38.708,32.664,42.726,31,47,31c4.274,0,8.292,1.664,11.314,4.686 + c6.238,6.238,6.238,16.389,0,22.627C55.292,61.336,51.274,63,47,63C42.726,63,38.708,61.336,35.686,58.314z"/> + </g> +</g> +</svg> diff --git a/tests/manual/examples/quick/customdialogs/index.html b/tests/manual/examples/quick/customdialogs/index.html new file mode 100644 index 000000000..d5de2827c --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/index.html @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="utf-8"> + <title>Custom UI</title> + <link rel="stylesheet" type="text/css" href="style.css"> + </head> + <body> + <table align="center"> + <tr> + <td><div class="div"><a href="#openMenu" class="link">Right click on text to see link context menu</a></div></td> + </tr> + <tr> + <td><div class="div"><p title="I am a tooltip.">Hover this text to display a tooltip</a></div></td> + </tr> + <tr> + <td><div class="div"><p>Touch devices only: long press on this text to see the touch selection menu</p></div></td> + </tr> + <tr> + <td><button class="button" onclick="window.location = 'http://localhost.:5555/OPEN_AUTH'"> + Open Authentication Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="window.location = 'http://www.qt.io'"> + Open Proxy Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="alert('This is the Alert Dialog !')"> + Open Alert Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="confirm('This is the Confirm Dialog.')"> + Open Confirm Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="prompt('Is this the Prompt Dialog ?', 'Yes')"> + Open Prompt Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="document.getElementById('colorpicker').click()"> + Open Color Dialog</button></td> + </tr> + <tr> + <td><button class="button" onclick="document.getElementById('filepicker').click()"> + Open File Dialog</button></td> + </tr> + </table> + <input type="color" id="colorpicker" value="#ff0000" style="visibility:hidden"/> + <input type="file" id="filepicker" accept=".cpp, .html, .h, .png, .qdoc, .qml" style="visibility:hidden"/> + </body> +</html> diff --git a/tests/manual/examples/quick/customdialogs/main.cpp b/tests/manual/examples/quick/customdialogs/main.cpp new file mode 100644 index 000000000..c114ea935 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/main.cpp @@ -0,0 +1,32 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "server.h" +#include <QtWebEngineQuick/qtwebenginequickglobal.h> +#include <QNetworkProxy> +#include <QQmlApplicationEngine> +#include <QTimer> +#include <QtGui/QGuiApplication> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + Server *server = new Server(&engine); + + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + QTimer::singleShot(0, server, &Server::run); + + QNetworkProxy proxy; + proxy.setType(QNetworkProxy::HttpProxy); + proxy.setHostName("localhost"); + proxy.setPort(5555); + QNetworkProxy::setApplicationProxy(proxy); + + return app.exec(); +} + diff --git a/tests/manual/examples/quick/customdialogs/main.qml b/tests/manual/examples/quick/customdialogs/main.qml new file mode 100644 index 000000000..d0cb6f324 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/main.qml @@ -0,0 +1,56 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import QtQuick.Window + +Window { + id: mainWindow + width: 800 + height: 610 + visible: true + + StackView { + id: stackView + anchors.fill: parent + focus: true + initialItem: Item { + id: main + width: mainWindow.width + height: mainWindow.height + ColumnLayout { + anchors.fill: parent + SwitchButton { + id: switcher + Layout.fillWidth: true + } + WebView { + id: webView + useDefaultDialogs: switcher.checked + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + + function closeForm() + { + pop(main); + // reset url in case of proxy error + webView.url = "qrc:/index.html" + } + + function openForm(form) + { + push(form.item, form.properties); + currentItem.closeForm.connect(closeForm); + } + + } + + Component.onCompleted: { + webView.openForm.connect(stackView.openForm); + } +} diff --git a/tests/manual/examples/quick/customdialogs/server.cpp b/tests/manual/examples/quick/customdialogs/server.cpp new file mode 100644 index 000000000..efb870618 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/server.cpp @@ -0,0 +1,47 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "server.h" +#include <QDataStream> +#include <QTcpSocket> + +Server::Server(QObject *parent) : QObject(parent) +{ + connect(&m_server, &QTcpServer::newConnection, this, &Server::handleNewConnection); +} + +void Server::run() +{ + if (!m_server.listen(QHostAddress::LocalHost, 5555)) + qWarning() << "Could not start the server -> http/proxy authentication dialog" + " will not work. Error:" << m_server.errorString(); +} + +void Server::handleNewConnection() +{ + QTcpSocket *socket = m_server.nextPendingConnection(); + connect(socket, &QAbstractSocket::disconnected, socket, &QObject::deleteLater); + connect(socket, &QAbstractSocket::readyRead, this, &Server::handleReadReady); +} + +void Server::handleReadReady() +{ + QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender()); + Q_ASSERT(socket); + m_data.append(socket->readAll()); + + // simply wait for whole request + if (!m_data.endsWith("\r\n\r\n")) + return; + if (m_data.contains(QByteArrayLiteral("OPEN_AUTH"))) { + socket->write("HTTP/1.1 401 Unauthorized\nWWW-Authenticate: " + "Basic realm=\"Very Restricted Area\"\r\n\r\n"); + m_data.clear(); + return; + } + + socket->write("HTTP/1.1 407 Proxy Auth Required\nProxy-Authenticate: " + "Basic realm=\"Proxy requires authentication\"\r\n" + "content-length: 0\r\n\r\n"); + m_data.clear(); +} diff --git a/tests/manual/examples/quick/customdialogs/server.h b/tests/manual/examples/quick/customdialogs/server.h new file mode 100644 index 000000000..563465013 --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/server.h @@ -0,0 +1,29 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#ifndef SERVER_H +#define SERVER_H + +#include <QObject> +#include <QTcpServer> + +class Server : public QObject +{ + Q_OBJECT + +public: + explicit Server(QObject *parent = nullptr); + +public slots: + void run(); + +private slots: + void handleNewConnection(); + void handleReadReady(); + +private: + QTcpServer m_server; + QByteArray m_data; +}; + +#endif // SERVER_H diff --git a/tests/manual/examples/quick/customdialogs/style.css b/tests/manual/examples/quick/customdialogs/style.css new file mode 100644 index 000000000..e4c25e7eb --- /dev/null +++ b/tests/manual/examples/quick/customdialogs/style.css @@ -0,0 +1,37 @@ +.div { + padding:8px 4px; + border: 5px solid #188BD0; + width: 280px; + font-family: sans-serif; + font-size:10pt; +} +.link { + text-decoration: none; + color: #888888; +} +.button { + background: -webkit-linear-gradient(top,#25A6E2 0%,#188BD0 100%); + padding:8px 13px; + color:#fff; + font-family: sans-serif; + font-size:17px; + -webkit-border-radius:5px; + border:1px solid #1A87FF; + width: 300px; +} +.button:focus { + outline: none; +} +.button:active { + background: -webkit-linear-gradient(top,#25A6E2 0%,#188BD0 70%); +} +.input { + padding:8px 4px; + border: 5px solid #188BD0; + width: 280px; + font-family: sans-serif; + font-size:10pt; +} +.input:focus { + outline: none; +} diff --git a/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt b/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt new file mode 100644 index 000000000..5574c28b8 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(customtouchhandle LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(customtouchhandle + SOURCES main.cpp +) + +set_target_properties(customtouchhandle PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(customtouchhandle PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt6_add_resources(customtouchhandle "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) + diff --git a/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro b/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro new file mode 100644 index 000000000..a74ef3146 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/customtouchhandle.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webenginequick + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/customtouchhandle +INSTALLS += target diff --git a/tests/manual/examples/quick/customtouchhandle/main.cpp b/tests/manual/examples/quick/customtouchhandle/main.cpp new file mode 100644 index 000000000..f1b70b024 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/main.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QtWebEngineQuick::initialize(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/customtouchhandle/main.qml b/tests/manual/examples/quick/customtouchhandle/main.qml new file mode 100644 index 000000000..c40b4c73b --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/main.qml @@ -0,0 +1,96 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine +import QtQuick.Layouts +import QtQuick.Controls + +ApplicationWindow { + width: 1024 + height: 750 + visible: true + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = text + } + + Label { text: 'Handle: ' } + ComboBox { + model: [ 'Default', 'Circle', 'Square' ] + + onCurrentValueChanged: { + if (currentValue == 'Circle') + webEngineView.touchHandleDelegate = circleTouchHandle + else if (currentValue == 'Square') + webEngineView.touchHandleDelegate = rectTouchHandle + else + webEngineView.touchHandleDelegate = null + } + + Component.onCompleted: currentIndex = indexOfValue('Square') + } + } + } + + Component { + id: circleTouchHandle + Rectangle { + color: "blue" + border.color: "black" + border.width: 2 + radius: 50 + } + } + + Component { + id: rectTouchHandle + Rectangle { + border.color: "black" + border.width: 2 + radius: 2 + onVisibleChanged: if (visible) { color = 'yellow'; cAnim.restart(); } + ColorAnimation on color { id: cAnim; to: 'red'; duration: 1000 } + } + } + + WebEngineView { + anchors.fill: parent + id: webEngineView + url: "https://www.qt.io" + } +} diff --git a/tests/manual/examples/quick/customtouchhandle/qml.qrc b/tests/manual/examples/quick/customtouchhandle/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/examples/quick/customtouchhandle/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/examples/quick/minimal/CMakeLists.txt b/tests/manual/examples/quick/minimal/CMakeLists.txt new file mode 100644 index 000000000..42e2078f7 --- /dev/null +++ b/tests/manual/examples/quick/minimal/CMakeLists.txt @@ -0,0 +1,36 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(minimal LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(webengine-minimal-qml + SOURCES + main.cpp +) + +set_target_properties(webengine-minimal-qml PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(webengine-minimal-qml PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt_add_resources(webengine-minimal-qml "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) diff --git a/tests/manual/examples/quick/minimal/main.cpp b/tests/manual/examples/quick/minimal/main.cpp new file mode 100644 index 000000000..16466ae06 --- /dev/null +++ b/tests/manual/examples/quick/minimal/main.cpp @@ -0,0 +1,19 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); + QtWebEngineQuick::initialize(); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/minimal/main.qml b/tests/manual/examples/quick/minimal/main.qml new file mode 100644 index 000000000..6890b501b --- /dev/null +++ b/tests/manual/examples/quick/minimal/main.qml @@ -0,0 +1,16 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine + +Window { + width: 1024 + height: 750 + visible: true + WebEngineView { + anchors.fill: parent + url: "chrome://qt" + } +} diff --git a/tests/manual/examples/quick/minimal/minimal.pro b/tests/manual/examples/quick/minimal/minimal.pro new file mode 100644 index 000000000..acca6477c --- /dev/null +++ b/tests/manual/examples/quick/minimal/minimal.pro @@ -0,0 +1,10 @@ +TEMPLATE = app + +QT += webenginequick + +SOURCES += main.cpp + +RESOURCES += qml.qrc + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginequick/minimal +INSTALLS += target diff --git a/tests/manual/examples/quick/minimal/qml.qrc b/tests/manual/examples/quick/minimal/qml.qrc new file mode 100644 index 000000000..0ff3892d9 --- /dev/null +++ b/tests/manual/examples/quick/minimal/qml.qrc @@ -0,0 +1,6 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> + diff --git a/tests/manual/examples/quick/webengineaction/CMakeLists.txt b/tests/manual/examples/quick/webengineaction/CMakeLists.txt new file mode 100644 index 000000000..e16772faf --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/CMakeLists.txt @@ -0,0 +1,37 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(webengineaction LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(webengineaction + SOURCES + main.cpp + utils.h +) + +set_target_properties(webengineaction PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(webengineaction PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineQuick +) + +# Resources: +set(qml_resource_files + "main.qml" +) + +qt_add_resources(webengineaction "qml" + PREFIX + "/" + FILES + ${qml_resource_files} +) diff --git a/tests/manual/examples/quick/webengineaction/main.cpp b/tests/manual/examples/quick/webengineaction/main.cpp new file mode 100644 index 000000000..a7bfaaf36 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/main.cpp @@ -0,0 +1,25 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include "utils.h" + +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + Utils utils; + + engine.rootContext()->setContextProperty("utils", &utils); + engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); + + return app.exec(); +} diff --git a/tests/manual/examples/quick/webengineaction/main.qml b/tests/manual/examples/quick/webengineaction/main.qml new file mode 100644 index 000000000..a1483b126 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/main.qml @@ -0,0 +1,119 @@ +// Copyright (C) 2018 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +import QtQuick +import QtQuick.Window +import QtWebEngine +import QtQuick.Controls +import QtQuick.Layouts + +ApplicationWindow { + id: window + visible: true + width: 800 + height: 600 + title: qsTr("WebEngineAction Example") + + header: ToolBar { + RowLayout { + anchors.fill: parent + + ToolButton { + property int itemAction: WebEngineView.Back + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: WebEngineView.Forward + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + ToolButton { + property int itemAction: webEngineView.loading ? WebEngineView.Stop : WebEngineView.Reload + text: webEngineView.action(itemAction).text + enabled: webEngineView.action(itemAction).enabled + onClicked: webEngineView.action(itemAction).trigger() + icon.name: webEngineView.action(itemAction).iconName + display: AbstractButton.TextUnderIcon + } + + TextField { + Layout.fillWidth: true + + text: webEngineView.url + selectByMouse: true + onEditingFinished: webEngineView.url = utils.fromUserInput(text) + } + + ToolButton { + id: settingsButton + text: "Settings" + icon.name: "settings-configure" + display: AbstractButton.TextUnderIcon + onClicked: settingsMenu.open() + checked: settingsMenu.visible + + Menu { + id: settingsMenu + y: settingsButton.height + + MenuItem { + id: customContextMenuOption + checkable: true + checked: true + + text: "Custom context menu" + } + } + } + } + } + + WebEngineView { + id: webEngineView + url: "https://qt.io" + anchors.fill: parent + + Component.onCompleted: { + profile.downloadRequested.connect(function(download){ + download.accept(); + }) + } + + property Menu contextMenu: Menu { + Repeater { + model: [ + WebEngineView.Back, + WebEngineView.Forward, + WebEngineView.Reload, + WebEngineView.SavePage, + WebEngineView.Copy, + WebEngineView.Paste, + WebEngineView.Cut + ] + MenuItem { + text: webEngineView.action(modelData).text + enabled: webEngineView.action(modelData).enabled + onClicked: webEngineView.action(modelData).trigger() + icon.name: webEngineView.action(modelData).iconName + display: MenuItem.TextBesideIcon + } + } + } + + onContextMenuRequested: function(request) { + if (customContextMenuOption.checked) { + request.accepted = true; + contextMenu.popup(); + } + } + } +} diff --git a/tests/manual/examples/quick/webengineaction/qml.qrc b/tests/manual/examples/quick/webengineaction/qml.qrc new file mode 100644 index 000000000..5f6483ac3 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/qml.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>main.qml</file> + </qresource> +</RCC> diff --git a/tests/manual/quick/touchbrowser/utils.h b/tests/manual/examples/quick/webengineaction/utils.h index 605ebf23d..d9a803907 100644 --- a/tests/manual/quick/touchbrowser/utils.h +++ b/tests/manual/examples/quick/webengineaction/utils.h @@ -1,5 +1,5 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #ifndef UTILS_H #define UTILS_H @@ -7,13 +7,14 @@ #include <QtCore/QFileInfo> #include <QtCore/QUrl> -class Utils : public QObject { +class Utils : public QObject +{ Q_OBJECT public: - Q_INVOKABLE static QUrl fromUserInput(const QString& userInput); + Q_INVOKABLE static QUrl fromUserInput(const QString &userInput); }; -inline QUrl Utils::fromUserInput(const QString& userInput) +inline QUrl Utils::fromUserInput(const QString &userInput) { QFileInfo fileInfo(userInput); if (fileInfo.exists()) diff --git a/tests/manual/examples/quick/webengineaction/webengineaction.pro b/tests/manual/examples/quick/webengineaction/webengineaction.pro new file mode 100644 index 000000000..9286604a1 --- /dev/null +++ b/tests/manual/examples/quick/webengineaction/webengineaction.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += webenginequick + +HEADERS += utils.h +SOURCES += main.cpp + +RESOURCES += qml.qrc diff --git a/tests/manual/examples/widgets/CMakeLists.txt b/tests/manual/examples/widgets/CMakeLists.txt new file mode 100644 index 000000000..d853ba634 --- /dev/null +++ b/tests/manual/examples/widgets/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(minimal) diff --git a/tests/manual/examples/widgets/minimal/CMakeLists.txt b/tests/manual/examples/widgets/minimal/CMakeLists.txt new file mode 100644 index 000000000..3b39683ba --- /dev/null +++ b/tests/manual/examples/widgets/minimal/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(minimal LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) + endif() + +qt_internal_add_manual_test(minimal-widgets + SOURCES + main.cpp +) + +set_target_properties(minimal-widgets PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE +) + +target_link_libraries(minimal-widgets PUBLIC + Qt::Core + Qt::Gui + Qt::WebEngineWidgets +) diff --git a/tests/manual/examples/widgets/minimal/main.cpp b/tests/manual/examples/widgets/minimal/main.cpp new file mode 100644 index 000000000..425973116 --- /dev/null +++ b/tests/manual/examples/widgets/minimal/main.cpp @@ -0,0 +1,28 @@ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QApplication> +#include <QWebEngineView> + +QUrl commandLineUrlArgument() +{ + const QStringList args = QCoreApplication::arguments(); + for (const QString &arg : args.mid(1)) { + if (!arg.startsWith(QLatin1Char('-'))) + return QUrl::fromUserInput(arg); + } + return QUrl(QStringLiteral("chrome://qt")); +} + +int main(int argc, char *argv[]) +{ + QCoreApplication::setOrganizationName("QtExamples"); + QApplication app(argc, argv); + + QWebEngineView view; + view.setUrl(commandLineUrlArgument()); + view.resize(1024, 750); + view.show(); + + return app.exec(); +} diff --git a/tests/manual/examples/widgets/minimal/minimal.pro b/tests/manual/examples/widgets/minimal/minimal.pro new file mode 100644 index 000000000..849f4b9b6 --- /dev/null +++ b/tests/manual/examples/widgets/minimal/minimal.pro @@ -0,0 +1,8 @@ +TEMPLATE = app + +QT += webenginewidgets + +SOURCES += main.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/webenginewidgets/minimal +INSTALLS += target diff --git a/tests/manual/manual.pro b/tests/manual/manual.pro deleted file mode 100644 index edf95846c..000000000 --- a/tests/manual/manual.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - widgets \ - quick - -!qtHaveModule(webenginewidgets): SUBDIRS -= widgets diff --git a/tests/manual/quick/CMakeLists.txt b/tests/manual/quick/CMakeLists.txt index 0562237b4..d6c4b88a9 100644 --- a/tests/manual/quick/CMakeLists.txt +++ b/tests/manual/quick/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(touchbrowser) +add_subdirectory(geopermission) diff --git a/tests/manual/quick/geopermission/CMakeLists.txt b/tests/manual/quick/geopermission/CMakeLists.txt new file mode 100644 index 000000000..088f248e1 --- /dev/null +++ b/tests/manual/quick/geopermission/CMakeLists.txt @@ -0,0 +1,63 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_geopermission LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(tst_geopermission + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Qml + Qt::Quick + Qt::WebEngineQuick +) + +if(WIN32) + set_property( + TARGET tst_geopermission + APPEND PROPERTY + SOURCES tst_geopermission.exe.manifest) +endif() + +set_target_properties(tst_geopermission PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.dev.webenginequick.tst_geopermission" +) + +# Resources: +set(resources_resource_files + "tst_geopermission.qml" + "geolocation.html" +) + +qt_add_resources(tst_geopermission "resources" + PREFIX + "/" + FILES + ${resources_resource_files} +) + +foreach(permission_plugin IN LISTS QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_permissions) + set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin}") + qt6_import_plugins(tst_geopermission INCLUDE ${permission_plugin}) +endforeach() + +if (APPLE) + set_target_properties(tst_geopermission PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + ) + + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + # Need to sign application for location permissions to work + add_custom_command(TARGET tst_geopermission + POST_BUILD COMMAND codesign -s - tst_geopermission.app) + endif() +endif() + diff --git a/tests/manual/quick/geopermission/Info.plist b/tests/manual/quick/geopermission/Info.plist new file mode 100644 index 000000000..9853e1900 --- /dev/null +++ b/tests/manual/quick/geopermission/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>NSLocationUsageDescription</key> + <string>Geolocation test would like to give web sites access to your location for demo purposes.</string> +</dict> +</plist> diff --git a/tests/manual/quick/geopermission/geolocation.html b/tests/manual/quick/geopermission/geolocation.html new file mode 100644 index 000000000..e8c54bc58 --- /dev/null +++ b/tests/manual/quick/geopermission/geolocation.html @@ -0,0 +1,32 @@ +<html> +<head> +<title>Geolocation Permission API Test</title> +<script> + +var errorMessage; +var handled = false; + +function successHandler(location) { + var message = document.getElementById("message"); + message.innerHTML = "Latitude: " + location.coords.latitude + + "<br>Longitude: " + location.coords.longitude; + + errorMessage = ""; + handled = true; +} + +function errorHandler(error) { + errorMessage = error.message; + handled = true; +} + +<!-- One shot example --> +navigator.geolocation.getCurrentPosition(successHandler, errorHandler); + +</script> +</head> +<body> +<div id="message">Location unknown</div> +</body> +</html> + diff --git a/tests/manual/quick/geopermission/main.cpp b/tests/manual/quick/geopermission/main.cpp new file mode 100644 index 000000000..e0ff6f3e7 --- /dev/null +++ b/tests/manual/quick/geopermission/main.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QtWebEngineQuick/qtwebenginequickglobal.h> + +#include <QtQml/QQmlApplicationEngine> +#include <QtQml/QQmlContext> +#include <QQuickView> + +#include <QtGui/QGuiApplication> + +#include <QtCore/QCommandLineParser> +#include <QtCore/QCommandLineOption> +#include <QtCore/QLoggingCategory> + +int main(int argc, char **argv) +{ + QCoreApplication::setApplicationName("Geopermission test"); + QCoreApplication::setOrganizationName("QtProject"); + + QtWebEngineQuick::initialize(); + + QGuiApplication app(argc, argv); + + QQuickView view; + + view.setTitle("Touch Browser"); + view.setFlags(Qt::Window | Qt::WindowTitleHint); + view.setResizeMode(QQuickView::SizeRootObjectToView); + view.setSource(QUrl("qrc:/tst_geopermission.qml")); + + QObject::connect(view.engine(), SIGNAL(quit()), &app, SLOT(quit())); + + view.show(); + if (view.size().isEmpty()) + view.setGeometry(0, 0, 800, 600); + + return app.exec(); +} diff --git a/tests/manual/quick/geopermission/tst_geopermission.qml b/tests/manual/quick/geopermission/tst_geopermission.qml new file mode 100644 index 000000000..ace48d464 --- /dev/null +++ b/tests/manual/quick/geopermission/tst_geopermission.qml @@ -0,0 +1,28 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +import QtQuick +import QtTest +import QtWebEngine + +WebEngineView { + id: webEngineView + width: 200 + height: 200 + url: Qt.resolvedUrl("qrc:/geolocation.html") + property bool deniedGeolocation: false + property bool geoPermissionRequested: false + + onPermissionRequested: function(perm) { + if (perm.feature === WebEnginePermission.Geolocation) { + geoPermissionRequested = true + if (deniedGeolocation) { + perm.deny() + } + else { + perm.grant() + } + } + } + +} diff --git a/tests/manual/quick/pdf/multipleDocuments.qml b/tests/manual/quick/pdf/multipleDocuments.qml index 9d08178f0..055808ab6 100644 --- a/tests/manual/quick/pdf/multipleDocuments.qml +++ b/tests/manual/quick/pdf/multipleDocuments.qml @@ -44,7 +44,7 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomIn enabled: pageView.sourceSize.width < 10000 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-in.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" onTriggered: pageView.renderScale *= root.scaleStep } } @@ -52,20 +52,20 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomOut enabled: pageView.sourceSize.width > 50 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-out.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" onTriggered: pageView.renderScale /= root.scaleStep } } ToolButton { action: Action { shortcut: "Ctrl+0" - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-original.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-original.svg" onTriggered: pageView.resetScale() } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-previous-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" enabled: pageView.backEnabled onTriggered: pageView.back() } @@ -84,7 +84,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-next-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-next-view-page.svg" enabled: pageView.forwardEnabled onTriggered: pageView.forward() } diff --git a/tests/manual/quick/pdf/pdfPageView.qml b/tests/manual/quick/pdf/pdfPageView.qml index 475e1d608..22c0d5ac2 100644 --- a/tests/manual/quick/pdf/pdfPageView.qml +++ b/tests/manual/quick/pdf/pdfPageView.qml @@ -24,7 +24,7 @@ ApplicationWindow { ToolButton { action: Action { shortcut: StandardKey.Open - icon.source: "../../../../examples/pdf/pdfviewer/resources/document-open.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/document-open.svg" onTriggered: fileDialog.open() } } @@ -32,7 +32,7 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomIn enabled: pageView.sourceSize.width < 10000 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-in.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-in.svg" onTriggered: pageView.renderScale *= root.scaleStep } } @@ -40,46 +40,46 @@ ApplicationWindow { action: Action { shortcut: StandardKey.ZoomOut enabled: pageView.sourceSize.width > 50 - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-out.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-out.svg" onTriggered: pageView.renderScale /= root.scaleStep } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-fit-width.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-fit-width.svg" onTriggered: pageView.scaleToWidth(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-fit-best.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-fit-best.svg" onTriggered: pageView.scaleToPage(root.contentItem.width, root.contentItem.height) } } ToolButton { action: Action { shortcut: "Ctrl+0" - icon.source: "../../../../examples/pdf/pdfviewer/resources/zoom-original.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/zoom-original.svg" onTriggered: pageView.resetScale() } } ToolButton { action: Action { shortcut: "Ctrl+L" - icon.source: "../../../../examples/pdf/pdfviewer/resources/rotate-left.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-left.svg" onTriggered: pageView.rotation -= 90 } } ToolButton { action: Action { shortcut: "Ctrl+R" - icon.source: "../../../../examples/pdf/pdfviewer/resources/rotate-right.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/rotate-right.svg" onTriggered: pageView.rotation += 90 } } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-previous-view-page.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-previous-view-page.svg" enabled: pageView.backEnabled onTriggered: pageView.back() } @@ -115,7 +115,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-next-view-page.svg" + icon.source: "../../../../examples/pdf//resources/go-next-view-page.svg" enabled: pageView.forwardEnabled onTriggered: pageView.forward() } @@ -126,7 +126,7 @@ ApplicationWindow { ToolButton { action: Action { shortcut: StandardKey.Copy - icon.source: "../../../../examples/pdf/pdfviewer/resources/edit-copy.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/edit-copy.svg" enabled: pageView.selectedText !== "" onTriggered: pageView.copySelectionToClipboard() } @@ -246,7 +246,7 @@ ApplicationWindow { RowLayout { ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-up-search.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-up-search.svg" shortcut: StandardKey.FindPrevious onTriggered: pageView.searchBack() } @@ -261,7 +261,7 @@ ApplicationWindow { Layout.fillWidth: true Image { visible: searchField.text !== "" - source: "../../../../examples/pdf/pdfviewer/resources/edit-clear.svg" + source: "../../../../examples/pdf/singlepage/resources/edit-clear.svg" anchors { right: parent.right top: parent.top @@ -276,7 +276,7 @@ ApplicationWindow { } ToolButton { action: Action { - icon.source: "../../../../examples/pdf/pdfviewer/resources/go-down-search.svg" + icon.source: "../../../../examples/pdf/singlepage/resources/go-down-search.svg" shortcut: StandardKey.FindNext onTriggered: pageView.searchForward() } diff --git a/tests/manual/quick/pdf/withdoc.qml b/tests/manual/quick/pdf/withdoc.qml index d4bf12e29..0a2a86630 100644 --- a/tests/manual/quick/pdf/withdoc.qml +++ b/tests/manual/quick/pdf/withdoc.qml @@ -19,6 +19,11 @@ Window { onPasswordRequired: function() { passwordDialog.open() } } + Component.onCompleted: { + if (Application.arguments.length > 2) + doc.source = Application.arguments[Application.arguments.length - 1] + } + FileDialog { id: fileDialog title: "Open a PDF file" @@ -85,6 +90,7 @@ Window { PdfPageImage { id: image document: doc + retainWhileLoading: true property real zoomFactor: Math.sqrt(2) diff --git a/tests/manual/quick/quick.pro b/tests/manual/quick/quick.pro deleted file mode 100644 index 8522763f8..000000000 --- a/tests/manual/quick/quick.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE = subdirs - -SUBDIRS += \ - faviconbrowser \ - touchbrowser diff --git a/tests/manual/quick/touchbrowser/AddressBar.qml b/tests/manual/quick/touchbrowser/AddressBar.qml index 6ccea3441..42188c94e 100644 --- a/tests/manual/quick/touchbrowser/AddressBar.qml +++ b/tests/manual/quick/touchbrowser/AddressBar.qml @@ -1,5 +1,5 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// 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 import QtQuick import QtQuick.Controls diff --git a/tests/manual/quick/touchbrowser/CMakeLists.txt b/tests/manual/quick/touchbrowser/CMakeLists.txt index f5cce07a3..0d3275e58 100644 --- a/tests/manual/quick/touchbrowser/CMakeLists.txt +++ b/tests/manual/quick/touchbrowser/CMakeLists.txt @@ -1,41 +1,31 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) - cmake_minimum_required(VERSION 3.16) + cmake_minimum_required(VERSION 3.19) project(touchbrowser LANGUAGES CXX) find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) endif() -if(NOT CMAKE_CROSSCOMPILING) - add_definitions(-DDESKTOP_BUILD) - set(SOURCES "touchmockingapplication.cpp") -endif() +set(CMAKE_AUTORCC ON) +set(TOUCHMOCKING_DIR "../../touchmocking") + +include_directories(${TOUCHMOCKING_DIR}) +add_definitions(-DQUICK_TOUCHBROWSER) -qt_internal_add_manual_test(touchbrowser +qt_internal_add_manual_test(touchbrowser-quick GUI SOURCES main.cpp - utils.h - ${SOURCES} + resources.qrc + ${TOUCHMOCKING_DIR}/touchmockingapplication.cpp + ${TOUCHMOCKING_DIR}/touchmockingapplication.h + ${TOUCHMOCKING_DIR}/utils.h LIBRARIES - Qt::GuiPrivate + Qt::Core Qt::Quick Qt::WebEngineQuick ENABLE_AUTOGEN_TOOLS moc ) -set(touchbrowser_resource_files - "AddressBar.qml" - "main.qml" - "MockTouchPoint.qml" - "touchpoint.png" -) - -qt_add_resources(touchbrowser "touchbrowser" - PREFIX - "/" - FILES - ${touchbrowser_resource_files} -) diff --git a/tests/manual/quick/touchbrowser/MockTouchPoint.qml b/tests/manual/quick/touchbrowser/MockTouchPoint.qml index bdce0555c..895e12e70 100644 --- a/tests/manual/quick/touchbrowser/MockTouchPoint.qml +++ b/tests/manual/quick/touchbrowser/MockTouchPoint.qml @@ -1,6 +1,5 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only - +// 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 import QtQuick Item { diff --git a/tests/manual/quick/touchbrowser/main.cpp b/tests/manual/quick/touchbrowser/main.cpp index b63f3b31c..1f4d7d869 100644 --- a/tests/manual/quick/touchbrowser/main.cpp +++ b/tests/manual/quick/touchbrowser/main.cpp @@ -1,23 +1,21 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// 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 -#if defined(DESKTOP_BUILD) #include "touchmockingapplication.h" -#endif #include "utils.h" -#include <QtGui/QGuiApplication> -#include <QtQml/QQmlApplicationEngine> -#include <QtQml/QQmlContext> -#include <QtQuick/QQuickView> -#include <QtWebEngineQuick/qtwebenginequickglobal.h> +#include <QGuiApplication> +#include <QQmlApplicationEngine> +#include <QQmlContext> +#include <QQuickView> +#include <QtWebEngineQuick> static QUrl startupUrl() { QUrl ret; QStringList args(qApp->arguments()); args.takeFirst(); - for (const QString &arg : qAsConst(args)) { + for (const QString &arg : std::as_const(args)) { if (arg.startsWith(QLatin1Char('-'))) continue; ret = Utils::fromUserInput(arg); @@ -29,31 +27,10 @@ static QUrl startupUrl() int main(int argc, char **argv) { - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); - - // We use touch mocking on desktop and apply all the mobile switches. - QByteArrayList args = QByteArrayList() - << QByteArrayLiteral("--enable-embedded-switches") - << QByteArrayLiteral("--log-level=0"); - const int count = args.size() + argc; - QList<char*> qargv(count); - - qargv[0] = argv[0]; - for (int i = 0; i < args.size(); ++i) - qargv[i + 1] = args[i].data(); - for (int i = args.size() + 1; i < count; ++i) - qargv[i] = argv[i - args.size()]; - - int qAppArgCount = qargv.size(); - QtWebEngineQuick::initialize(); -#if defined(DESKTOP_BUILD) - TouchMockingApplication app(qAppArgCount, qargv.data()); -#else - QGuiApplication app(qAppArgCount, qargv.data()); -#endif + TouchMockingApplication app(argc, argv); + app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true); QQuickView view; Utils utils; diff --git a/tests/manual/quick/touchbrowser/qml.qrc b/tests/manual/quick/touchbrowser/resources.qrc index 5c384502e..87d655a27 100644 --- a/tests/manual/quick/touchbrowser/qml.qrc +++ b/tests/manual/quick/touchbrowser/resources.qrc @@ -2,7 +2,6 @@ <qresource prefix="/"> <file>main.qml</file> <file>AddressBar.qml</file> - <file>MockTouchPoint.qml</file> - <file>touchpoint.png</file> + <file alias="touchpoint.png">../../touchmocking/touchpoint.png</file> </qresource> </RCC> diff --git a/tests/manual/quick/touchbrowser/touchbrowser.pro b/tests/manual/quick/touchbrowser/touchbrowser.pro index 92e4e6703..710584df8 100644 --- a/tests/manual/quick/touchbrowser/touchbrowser.pro +++ b/tests/manual/quick/touchbrowser/touchbrowser.pro @@ -1,19 +1,15 @@ TEMPLATE = app -QT += quick webenginequick -CONFIG += c++11 +DEFINES += QUICK_TOUCHBROWSER +QT += core gui quick webenginequick -SOURCES += \ - main.cpp +INCLUDEPATH += ../../touchmocking +SOURCES += \ + main.cpp \ + ../../touchmocking/touchmockingapplication.cpp HEADERS += \ - utils.h - -RESOURCES += qml.qrc + ../../touchmocking/touchmockingapplication.h \ + ../../touchmocking/utils.h -!cross_compile { - DEFINES += DESKTOP_BUILD - SOURCES += touchmockingapplication.cpp - HEADERS += touchmockingapplication.h - QT += gui-private -} +RESOURCES += resources.qrc diff --git a/tests/manual/quick/touchbrowser/touchmockingapplication.cpp b/tests/manual/quick/touchbrowser/touchmockingapplication.cpp deleted file mode 100644 index 4fad86d33..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.cpp +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#include "touchmockingapplication.h" - -#include <qpa/qwindowsysteminterface.h> -#include <QtQuick/QQuickItem> -#include <QtQuick/QQuickView> -#include <QInputDevice> - -static inline bool isTouchEvent(const QEvent* event) -{ - switch (event->type()) { - case QEvent::TouchBegin: - case QEvent::TouchUpdate: - case QEvent::TouchEnd: - return true; - default: - return false; - } -} - -static inline bool isMouseEvent(const QEvent* event) -{ - switch (event->type()) { - case QEvent::MouseButtonPress: - case QEvent::MouseMove: - case QEvent::MouseButtonRelease: - case QEvent::MouseButtonDblClick: - return true; - default: - return false; - } -} - -TouchMockingApplication::TouchMockingApplication(int& argc, char** argv) - : QGuiApplication(argc, argv) - , m_realTouchEventReceived(false) - , m_pendingFakeTouchEventCount(0) - , m_holdingControl(false) -{ -} - -bool TouchMockingApplication::notify(QObject* target, QEvent* event) -{ - // We try to be smart, if we received real touch event, we are probably on a device - // with touch screen, and we should not have touch mocking. - - if (!event->spontaneous() || m_realTouchEventReceived) - return QGuiApplication::notify(target, event); - - if (isTouchEvent(event)) { - if (m_pendingFakeTouchEventCount) - --m_pendingFakeTouchEventCount; - else - m_realTouchEventReceived = true; - return QGuiApplication::notify(target, event); - } - - QQuickView* window = qobject_cast<QQuickView*>(target); - if (!window) - return QGuiApplication::notify(target, event); - - m_holdingControl = QGuiApplication::keyboardModifiers().testFlag(Qt::ControlModifier); - - if (event->type() == QEvent::KeyRelease && static_cast<QKeyEvent*>(event)->key() == Qt::Key_Control) { - foreach (int id, m_heldTouchPoints) - if (m_touchPoints.contains(id) && !QGuiApplication::mouseButtons().testFlag(Qt::MouseButton(id))) { - QMutableEventPoint::setState(m_touchPoints[id], QEventPoint::Released); - m_heldTouchPoints.remove(id); - } else - QMutableEventPoint::setState(m_touchPoints[id], QEventPoint::Stationary); - - sendTouchEvent(window, m_heldTouchPoints.isEmpty() ? QEvent::TouchEnd : QEvent::TouchUpdate, static_cast<QKeyEvent*>(event)->timestamp()); - } - - if (isMouseEvent(event)) { - const QMouseEvent* const mouseEvent = static_cast<QMouseEvent*>(event); - - QEventPoint touchPoint; - QMutableEventPoint::setPressure(touchPoint, 1); - - QEvent::Type touchType = QEvent::None; - - switch (mouseEvent->type()) { - case QEvent::MouseButtonPress: - QMutableEventPoint::setId(touchPoint, mouseEvent->button()); - if (m_touchPoints.contains(touchPoint.id())) { - QMutableEventPoint::setState(touchPoint, QEventPoint::Updated); - touchType = QEvent::TouchUpdate; - } else { - QMutableEventPoint::setState(touchPoint, QEventPoint::Pressed); - // Check if more buttons are held down than just the event triggering one. - if (mouseEvent->buttons() > mouseEvent->button()) - touchType = QEvent::TouchUpdate; - else - touchType = QEvent::TouchBegin; - } - break; - case QEvent::MouseMove: - if (!mouseEvent->buttons()) { - // We have to swallow the event instead of propagating it, - // since we avoid sending the mouse release events and if the - // Flickable is the mouse grabber it would receive the event - // and would move the content. - event->accept(); - return true; - } - touchType = QEvent::TouchUpdate; - QMutableEventPoint::setId(touchPoint, mouseEvent->buttons()); - QMutableEventPoint::setState(touchPoint, QEventPoint::Updated); - break; - case QEvent::MouseButtonRelease: - // Check if any buttons are still held down after this event. - if (mouseEvent->buttons()) - touchType = QEvent::TouchUpdate; - else - touchType = QEvent::TouchEnd; - QMutableEventPoint::setId(touchPoint, mouseEvent->button()); - QMutableEventPoint::setState(touchPoint, QEventPoint::Released); - break; - case QEvent::MouseButtonDblClick: - // Eat double-clicks, their accompanying press event is all we need. - event->accept(); - return true; - default: - Q_ASSERT_X(false, "multi-touch mocking", "unhandled event type"); - } - - // A move can have resulted in multiple buttons, so we need check them individually. - if (touchPoint.id() & Qt::LeftButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::LeftButton); - if (touchPoint.id() & Qt::MiddleButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::MiddleButton); - if (touchPoint.id() & Qt::RightButton) - updateTouchPoint(mouseEvent, touchPoint, Qt::RightButton); - - if (m_holdingControl && touchPoint.state() == QEventPoint::Released) { - // We avoid sending the release event because the Flickable is - // listening to mouse events and would start a bounce-back - // animation if it received a mouse release. - event->accept(); - return true; - } - - // Update states for all other touch-points - for (QHash<int, QEventPoint>::iterator it = m_touchPoints.begin(), end = m_touchPoints.end(); it != end; ++it) { - if (!(it.value().id() & touchPoint.id())) - QMutableEventPoint::setState(it.value(), QEventPoint::Stationary); - } - - Q_ASSERT(touchType != QEvent::None); - - if (!sendTouchEvent(window, touchType, mouseEvent->timestamp())) - return QGuiApplication::notify(target, event); - - event->accept(); - return true; - } - - return QGuiApplication::notify(target, event); -} - -void TouchMockingApplication::updateTouchPoint(const QMouseEvent* mouseEvent, QEventPoint touchPoint, Qt::MouseButton mouseButton) -{ - // Ignore inserting additional touch points if Ctrl isn't held because it produces - // inconsistent touch events and results in assers in the gesture recognizers. - if (!m_holdingControl && m_touchPoints.size() && !m_touchPoints.contains(mouseButton)) - return; - - if (m_holdingControl && touchPoint.state() == QEventPoint::Released) { - m_heldTouchPoints.insert(mouseButton); - return; - } - - // Gesture recognition uses the screen position for the initial threshold - // but since the canvas translates touch events we actually need to pass - // the screen position as the scene position to deliver the appropriate - // coordinates to the target. - QMutableEventPoint::setPosition(touchPoint, mouseEvent->position()); - QMutableEventPoint::setScenePosition(touchPoint, mouseEvent->globalPosition()); - - if (touchPoint.state() == QEventPoint::Pressed) - QMutableEventPoint::setScenePosition(touchPoint, mouseEvent->scenePosition()); - else { - const QEventPoint& oldTouchPoint = m_touchPoints[mouseButton]; - QMutableEventPoint::setGlobalLastPosition(touchPoint, oldTouchPoint.globalPosition()); - } - - // Update current touch-point. - QMutableEventPoint::setId(touchPoint, mouseButton); - m_touchPoints.insert(mouseButton, touchPoint); -} - -bool TouchMockingApplication::sendTouchEvent(QQuickView* window, QEvent::Type type, ulong timestamp) -{ - static QPointingDevice* device = 0; - if (!device) { - device = new QPointingDevice(QStringLiteral("MockTouchDevice"), 1, - QPointingDevice::DeviceType::TouchScreen, - QPointingDevice::PointerType::AllPointerTypes, - QInputDevice::Capability::All, 3, 3, - QString(), QPointingDeviceUniqueId(), window->rootObject()); - - QWindowSystemInterface::registerInputDevice(device); - } - - m_pendingFakeTouchEventCount++; - - const QList<QEventPoint>& currentTouchPoints = m_touchPoints.values(); - QEventPoint::States touchPointStates = QEventPoint::States(); - foreach (const QEventPoint& touchPoint, currentTouchPoints) - touchPointStates |= touchPoint.state(); - - QTouchEvent event(type, device, Qt::NoModifier, currentTouchPoints); - event.setTimestamp(timestamp); - event.setAccepted(false); - - QGuiApplication::notify(window, &event); - - updateVisualMockTouchPoints(window, m_holdingControl ? currentTouchPoints : QList<QEventPoint>()); - - // Get rid of touch-points that are no longer valid - foreach (const QEventPoint& touchPoint, currentTouchPoints) { - if (touchPoint.state() == QEventPoint::Released) - m_touchPoints.remove(touchPoint.id()); - } - - return event.isAccepted(); -} - -void TouchMockingApplication::updateVisualMockTouchPoints(QQuickView* window,const QList<QEventPoint>& touchPoints) -{ - if (touchPoints.isEmpty()) { - // Hide all touch indicator items. - foreach (QQuickItem* item, m_activeMockComponents.values()) - item->setProperty("pressed", false); - - return; - } - - foreach (const QEventPoint& touchPoint, touchPoints) { - QQuickItem* mockTouchPointItem = m_activeMockComponents.value(touchPoint.id()); - - if (!mockTouchPointItem) { - QQmlComponent touchMockPointComponent(window->engine(), QUrl("qrc:///MockTouchPoint.qml")); - mockTouchPointItem = qobject_cast<QQuickItem*>(touchMockPointComponent.create()); - Q_ASSERT(mockTouchPointItem); - m_activeMockComponents.insert(touchPoint.id(), mockTouchPointItem); - mockTouchPointItem->setProperty("pointId", QVariant(touchPoint.id())); - mockTouchPointItem->setParent(window->rootObject()); - mockTouchPointItem->setParentItem(window->rootObject()); - } - - mockTouchPointItem->setX(touchPoint.position().x()); - mockTouchPointItem->setY(touchPoint.position().y()); - mockTouchPointItem->setWidth(touchPoint.ellipseDiameters().width()); - mockTouchPointItem->setHeight(touchPoint.ellipseDiameters().height()); - mockTouchPointItem->setProperty("pressed", QVariant(touchPoint.state() != QEventPoint::Released)); - } -} diff --git a/tests/manual/quick/touchbrowser/touchmockingapplication.h b/tests/manual/quick/touchbrowser/touchmockingapplication.h deleted file mode 100644 index 3264d5a0c..000000000 --- a/tests/manual/quick/touchbrowser/touchmockingapplication.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -#ifndef TOUCHMOCKINGAPPLICATION_H -#define TOUCHMOCKINGAPPLICATION_H - -#include <QtGui/QGuiApplication> -#include <QtGui/private/qeventpoint_p.h> -#include <QtGui/QEventPoint> - -#include <private/qevent_p.h> - -QT_BEGIN_NAMESPACE -class QQuickView; -class QQuickItem; -QT_END_NAMESPACE - -class TouchMockingApplication : public QGuiApplication -{ - Q_OBJECT - -public: - TouchMockingApplication(int &argc, char **argv); - - virtual bool notify(QObject *, QEvent *) override; - -private: - void updateTouchPoint(const QMouseEvent *, QEventPoint, Qt::MouseButton); - bool sendTouchEvent(QQuickView *, QEvent::Type, ulong timestamp); - void updateVisualMockTouchPoints(QQuickView *,const QList<QEventPoint> &touchPoints); - -private: - bool m_realTouchEventReceived; - int m_pendingFakeTouchEventCount; - - QHash<int, QEventPoint> m_touchPoints; - QSet<int> m_heldTouchPoints; - QHash<int, QQuickItem*> m_activeMockComponents; - - bool m_holdingControl; -}; - -#endif // TOUCHMOCKINGAPPLICATION_H diff --git a/tests/manual/touchmocking/touchmockingapplication.cpp b/tests/manual/touchmocking/touchmockingapplication.cpp new file mode 100644 index 000000000..feedae5cd --- /dev/null +++ b/tests/manual/touchmocking/touchmockingapplication.cpp @@ -0,0 +1,78 @@ +// 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 "touchmockingapplication.h" + +#include <QCursor> +#include <QEvent> +#include <QPixmap> + +#if defined(QUICK_TOUCHBROWSER) +# include <QQuickView> +#endif + +#if defined(WIDGET_TOUCHBROWSER) +# include <QMainWindow> +#endif + +static inline bool isMouseEvent(QEvent *event) +{ + switch (event->type()) { + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + return true; + default: + return false; + } +} + +TouchMockingApplication::TouchMockingApplication(int &argc, char **argv) + : Application(argc, argv), m_touchPoint(new QCursor(QPixmap(":touchpoint.png"))) +{ +} + +TouchMockingApplication::~TouchMockingApplication() +{ + delete m_touchPoint; +} + +bool TouchMockingApplication::notify(QObject *target, QEvent *event) +{ + switch (event->type()) { + case QEvent::TouchBegin: + setOverrideCursor(*m_touchPoint); + break; + case QEvent::TouchEnd: + restoreCursor(); + break; + default: + break; + } + +// All mouse events that are not accepted by the application will be translated to touch events +// instead (see Qt::AA_SynthesizeTouchForUnhandledMouseEvents). +#if defined(QUICK_TOUCHBROWSER) + if (isMouseEvent(event) && qobject_cast<QQuickView *>(target)) { + event->ignore(); + return false; + } +#elif defined(WIDGET_TOUCHBROWSER) + // Popups ignore touch evenets so we send MouseEvents directly. + if (isMouseEvent(event)) { + if (activePopupWidget()) { + restoreCursor(); + } else { + event->ignore(); + return false; + } + } +#endif + return Application::notify(target, event); +} + +void TouchMockingApplication::restoreCursor() +{ + while (overrideCursor()) + restoreOverrideCursor(); +} diff --git a/tests/manual/touchmocking/touchmockingapplication.h b/tests/manual/touchmocking/touchmockingapplication.h new file mode 100644 index 000000000..4f6e744fc --- /dev/null +++ b/tests/manual/touchmocking/touchmockingapplication.h @@ -0,0 +1,35 @@ +// 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 TOUCHMOCKINGAPPLICATION_H +#define TOUCHMOCKINGAPPLICATION_H + +#if defined(QUICK_TOUCHBROWSER) +# include <QGuiApplication> +using Application = QGuiApplication; +#elif defined(WIDGET_TOUCHBROWSER) +# include <QApplication> +using Application = QApplication; +#endif + +QT_BEGIN_NAMESPACE +class QCursor; +QT_END_NAMESPACE + +class TouchMockingApplication : public Application +{ + Q_OBJECT + +public: + TouchMockingApplication(int &argc, char **argv); + ~TouchMockingApplication(); + + virtual bool notify(QObject *, QEvent *) override; + +private: + void restoreCursor(); + + QCursor *m_touchPoint; +}; + +#endif // TOUCHMOCKINGAPPLICATION_H diff --git a/tests/manual/quick/touchbrowser/touchpoint.png b/tests/manual/touchmocking/touchpoint.png Binary files differindex 7649ee991..7649ee991 100644 --- a/tests/manual/quick/touchbrowser/touchpoint.png +++ b/tests/manual/touchmocking/touchpoint.png diff --git a/tests/manual/touchmocking/utils.h b/tests/manual/touchmocking/utils.h new file mode 100644 index 000000000..12d493d3f --- /dev/null +++ b/tests/manual/touchmocking/utils.h @@ -0,0 +1,25 @@ +// 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 UTILS_H +#define UTILS_H + +#include <QFileInfo> +#include <QUrl> + +class Utils : public QObject +{ + Q_OBJECT +public: + Q_INVOKABLE static QUrl fromUserInput(const QString &userInput); +}; + +inline QUrl Utils::fromUserInput(const QString &userInput) +{ + QFileInfo fileInfo(userInput); + if (fileInfo.exists()) + return QUrl::fromLocalFile(fileInfo.absoluteFilePath()); + return QUrl::fromUserInput(userInput); +} + +#endif // UTILS_H diff --git a/tests/manual/widgets/CMakeLists.txt b/tests/manual/widgets/CMakeLists.txt index 4e619f066..7c19f9e43 100644 --- a/tests/manual/widgets/CMakeLists.txt +++ b/tests/manual/widgets/CMakeLists.txt @@ -1,2 +1,9 @@ add_subdirectory(inputmethods) -add_subdirectory(webgl) +add_subdirectory(geolocation) +add_subdirectory(touchbrowser) +if(QT_FEATURE_opengl) + add_subdirectory(webgl) +endif() +if(TARGET Qt6::HttpServer) + add_subdirectory(webrtc) +endif() diff --git a/tests/manual/widgets/geolocation/CMakeLists.txt b/tests/manual/widgets/geolocation/CMakeLists.txt new file mode 100644 index 000000000..2ca8c2f52 --- /dev/null +++ b/tests/manual/widgets/geolocation/CMakeLists.txt @@ -0,0 +1,55 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(geolocation LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_manual_test(geolocation + GUI + SOURCES + main.cpp + LIBRARIES + Qt::Core + Qt::Gui + Qt::Test + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) + +set_target_properties(geolocation PROPERTIES + WIN32_EXECUTABLE TRUE + MACOSX_BUNDLE TRUE + MACOSX_BUNDLE_GUI_IDENTIFIER "io.qt.dev.webenginewidgets.geolocation" +) + +set(geolocation_resource_files + "geolocation.html" +) + +qt_add_resources(geolocation "geolocation" + PREFIX + "/" + FILES + ${geolocation_resource_files} +) + + foreach(permission_plugin IN LISTS QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_permissions) + set(permission_plugin "${QT_CMAKE_EXPORT_NAMESPACE}::${permission_plugin}") + qt6_import_plugins(geolocation INCLUDE ${permission_plugin}) + endforeach() + +if (APPLE) + set_target_properties(geolocation PROPERTIES + MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" + ) + + if (NOT CMAKE_GENERATOR STREQUAL "Xcode") + # Need to sign application for location permissions to work + add_custom_command(TARGET geolocation + POST_BUILD COMMAND codesign -s - geolocation.app) + endif() +endif() diff --git a/tests/manual/widgets/geolocation/Info.plist b/tests/manual/widgets/geolocation/Info.plist new file mode 100644 index 000000000..9853e1900 --- /dev/null +++ b/tests/manual/widgets/geolocation/Info.plist @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + <key>CFBundlePackageType</key> + <string>APPL</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>LSMinimumSystemVersion</key> + <string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string> + <key>NSHumanReadableCopyright</key> + <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleDevelopmentRegion</key> + <string>English</string> + <key>NSSupportsAutomaticGraphicsSwitching</key> + <true/> + <key>NSLocationUsageDescription</key> + <string>Geolocation test would like to give web sites access to your location for demo purposes.</string> +</dict> +</plist> diff --git a/tests/manual/widgets/geolocation/geolocation.html b/tests/manual/widgets/geolocation/geolocation.html new file mode 100644 index 000000000..e8c54bc58 --- /dev/null +++ b/tests/manual/widgets/geolocation/geolocation.html @@ -0,0 +1,32 @@ +<html> +<head> +<title>Geolocation Permission API Test</title> +<script> + +var errorMessage; +var handled = false; + +function successHandler(location) { + var message = document.getElementById("message"); + message.innerHTML = "Latitude: " + location.coords.latitude + + "<br>Longitude: " + location.coords.longitude; + + errorMessage = ""; + handled = true; +} + +function errorHandler(error) { + errorMessage = error.message; + handled = true; +} + +<!-- One shot example --> +navigator.geolocation.getCurrentPosition(successHandler, errorHandler); + +</script> +</head> +<body> +<div id="message">Location unknown</div> +</body> +</html> + diff --git a/tests/manual/widgets/geolocation/main.cpp b/tests/manual/widgets/geolocation/main.cpp new file mode 100644 index 000000000..1f4cefe54 --- /dev/null +++ b/tests/manual/widgets/geolocation/main.cpp @@ -0,0 +1,48 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QApplication> +#include <QFormLayout> +#include <QGroupBox> +#include <QHBoxLayout> +#include <QLabel> +#include <QMainWindow> +#include <QMessageBox> +#include <QVBoxLayout> +#include <QWebEnginePage> +#include <QWebEngineView> + +class GeoPermissionWebView : public QWebEngineView { + Q_OBJECT + +public slots: + void handlePermissionRequested(QWebEnginePermission permission) + { + qWarning("Feature Permission"); + QString title = tr("Permission Request"); + QString question = QLatin1String("Allow access to geolocation?"); + if (!question.isEmpty() && QMessageBox::question(window(), title, question) == QMessageBox::Yes) + permission.grant(); + else + permission.deny(); + } + +}; + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + QMainWindow w; + GeoPermissionWebView webview; + QWebEnginePage page; + QObject::connect(&page, &QWebEnginePage::permissionRequested, &webview, + &GeoPermissionWebView::handlePermissionRequested); + webview.setPage(&page); + page.load(QUrl("qrc:/geolocation.html")); + w.setCentralWidget(&webview); + w.show(); + + return a.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/inputmethods/CMakeLists.txt b/tests/manual/widgets/inputmethods/CMakeLists.txt index 8b4a87cf5..acd5bca50 100644 --- a/tests/manual/widgets/inputmethods/CMakeLists.txt +++ b/tests/manual/widgets/inputmethods/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) cmake_minimum_required(VERSION 3.16) diff --git a/tests/manual/widgets/inputmethods/colorpicker.h b/tests/manual/widgets/inputmethods/colorpicker.h index 171c0186b..0b6b3257a 100644 --- a/tests/manual/widgets/inputmethods/colorpicker.h +++ b/tests/manual/widgets/inputmethods/colorpicker.h @@ -7,8 +7,10 @@ #include <QColor> #include <QWidget> +QT_BEGIN_NAMESPACE class QLineEdit; class QPushButton; +QT_END_NAMESPACE class ColorPicker : public QWidget { diff --git a/tests/manual/widgets/inputmethods/controlview.h b/tests/manual/widgets/inputmethods/controlview.h index f6b3e7fe6..caa08593f 100644 --- a/tests/manual/widgets/inputmethods/controlview.h +++ b/tests/manual/widgets/inputmethods/controlview.h @@ -8,12 +8,15 @@ #include <QTextCharFormat> #include <QWidget> -class ColorPicker; +QT_BEGIN_NAMESPACE class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QSpinBox; +QT_END_NAMESPACE + +class ColorPicker; class ControlView : public QWidget { diff --git a/tests/manual/widgets/inputmethods/testview.h b/tests/manual/widgets/inputmethods/testview.h index feb512cde..b99e60d75 100644 --- a/tests/manual/widgets/inputmethods/testview.h +++ b/tests/manual/widgets/inputmethods/testview.h @@ -7,8 +7,10 @@ #include <QTextCharFormat> #include <QWidget> +QT_BEGIN_NAMESPACE class QPushButton; class QTableView; +QT_END_NAMESPACE class TestView : public QWidget { diff --git a/tests/manual/widgets/touchbrowser/CMakeLists.txt b/tests/manual/widgets/touchbrowser/CMakeLists.txt new file mode 100644 index 000000000..de60ad2b4 --- /dev/null +++ b/tests/manual/widgets/touchbrowser/CMakeLists.txt @@ -0,0 +1,30 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.19) + project(touchbrowser LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +set(CMAKE_AUTORCC ON) +set(TOUCHMOCKING_DIR "../../touchmocking") + +include_directories(${TOUCHMOCKING_DIR}) +add_definitions(-DWIDGET_TOUCHBROWSER) + +qt_internal_add_manual_test(touchbrowser-widget + GUI + SOURCES + main.cpp + resources.qrc + ${TOUCHMOCKING_DIR}/touchmockingapplication.cpp + ${TOUCHMOCKING_DIR}/touchmockingapplication.h + ${TOUCHMOCKING_DIR}/utils.h + LIBRARIES + Qt::Core + Qt::Gui + Qt::WebEngineWidgets + ENABLE_AUTOGEN_TOOLS + moc +) diff --git a/tests/manual/widgets/touchbrowser/main.cpp b/tests/manual/widgets/touchbrowser/main.cpp new file mode 100644 index 000000000..18baf79e8 --- /dev/null +++ b/tests/manual/widgets/touchbrowser/main.cpp @@ -0,0 +1,62 @@ +// 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 "touchmockingapplication.h" +#include "utils.h" + +#include <QApplication> +#include <QLineEdit> +#include <QMainWindow> +#include <QToolBar> +#include <QWebEngineView> + + +static QUrl startupUrl() +{ + QUrl ret; + QStringList args(qApp->arguments()); + args.takeFirst(); + for (const QString &arg : std::as_const(args)) { + if (arg.startsWith(QLatin1Char('-'))) + continue; + ret = Utils::fromUserInput(arg); + if (ret.isValid()) + return ret; + } + return QUrl(QStringLiteral("https://www.qt.io/")); +} + +int main(int argc, char **argv) +{ + TouchMockingApplication app(argc, argv); + app.setAttribute(Qt::AA_SynthesizeTouchForUnhandledMouseEvents, true); + + QMainWindow window; + QWebEngineView view(&window); + QToolBar addressBar("AddressBar", &window); + QLineEdit lineEdit(&addressBar); + + view.setAttribute(Qt::WA_AcceptTouchEvents, true); + view.setUrl(startupUrl()); + window.resize(1024, 750); + window.setCentralWidget(&view); + + addressBar.setAttribute(Qt::WA_AcceptTouchEvents, true); + addressBar.setMovable(false); + addressBar.toggleViewAction()->setEnabled(false); + + lineEdit.setAttribute(Qt::WA_AcceptTouchEvents, true); + lineEdit.setClearButtonEnabled(true); + + addressBar.addWidget(&lineEdit); + QObject::connect(&lineEdit, &QLineEdit::returnPressed, [&]() { + QUrl url = Utils::fromUserInput(lineEdit.text()); + lineEdit.setText(url.toDisplayString()); + view.setUrl(url); + }); + + window.addToolBar(&addressBar); + window.show(); + + return app.exec(); +} diff --git a/tests/manual/widgets/touchbrowser/resources.qrc b/tests/manual/widgets/touchbrowser/resources.qrc new file mode 100644 index 000000000..b621823ea --- /dev/null +++ b/tests/manual/widgets/touchbrowser/resources.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file alias="touchpoint.png">../../touchmocking/touchpoint.png</file> + </qresource> +</RCC> diff --git a/tests/manual/widgets/touchbrowser/touchbrowser.pro b/tests/manual/widgets/touchbrowser/touchbrowser.pro new file mode 100644 index 000000000..1587f390a --- /dev/null +++ b/tests/manual/widgets/touchbrowser/touchbrowser.pro @@ -0,0 +1,15 @@ +TEMPLATE = app + +DEFINES += WIDGET_TOUCHBROWSER +QT += core gui webenginewidgets + +INCLUDEPATH += ../../touchmocking + +SOURCES += \ + main.cpp \ + ../../touchmocking/touchmockingapplication.cpp +HEADERS += \ + ../../touchmocking/touchmockingapplication.h \ + ../../touchmocking/utils.h + +RESOURCES += resources.qrc diff --git a/tests/manual/widgets/webgl/CMakeLists.txt b/tests/manual/widgets/webgl/CMakeLists.txt index bf037abd8..034a06a79 100644 --- a/tests/manual/widgets/webgl/CMakeLists.txt +++ b/tests/manual/widgets/webgl/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +# SPDX-License-Identifier: BSD-3-Clause if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) cmake_minimum_required(VERSION 3.16) diff --git a/tests/manual/widgets/webrtc/CMakeLists.txt b/tests/manual/widgets/webrtc/CMakeLists.txt new file mode 100644 index 000000000..3f98f1fd6 --- /dev/null +++ b/tests/manual/widgets/webrtc/CMakeLists.txt @@ -0,0 +1,24 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if (NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.19) + project(webrtc LANGUAGES CXX) + find_package(Qt6BuildInternals COMPONENTS STANDALONE_TEST) +endif() + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTORCC ON) + +qt_internal_add_manual_test(webrtc + GUI + SOURCES + main.cpp + mediaPicker.ui + qrc.qrc + LIBRARIES + Qt::Core + Qt::HttpServer + Qt::Gui + Qt::WebEngineWidgets +) diff --git a/tests/manual/widgets/webrtc/index.html b/tests/manual/widgets/webrtc/index.html new file mode 100644 index 000000000..433d643c3 --- /dev/null +++ b/tests/manual/widgets/webrtc/index.html @@ -0,0 +1,86 @@ +<!doctype html> +<html> + <head> + <style> + body { + display: flex; + justify-content: center; + align-items: center; + flex-wrap: wrap; + flex-flow: column; + } + buttons { + justify-content: space-around; + } + </style> + </head> + <body> + <div id="buttons" > + <input value ="getDisplayMedia" onclick="getDisplayMedia(true, true);" type="button"> + <input value = "chooseDesktopMedia" onclick="chooseDesktopMedia();" type="button"> + <input value ="Stop" onclick="stop();" type="button"> + </div> + <div id="content"></div> + </body> + <script> + const EXTENSION_ID = "nkeimhogjdpnpccoofpliimaahmaaome"; // hangout services extension + const content = document.getElementById("content"); + const video = document.createElement("video"); + video.setAttribute("width", 640); + video.setAttribute("height", 640); + video.setAttribute("style", "background-color: black;"); + content.appendChild(video); + + async function getDisplayMedia(v = true, a = true) { + stop(); + navigator.mediaDevices.getDisplayMedia({ video: v, audio: a }) + .then(stream => { + start(stream); + }, error => { + console.error(error); + }); + } + + function chooseDesktopMedia() { + stop(); + // Connect to the 'chooseDesktopMedia' listener within the hangout services extension. + let port = chrome.runtime.connect(EXTENSION_ID, {name: "chooseDesktopMedia"}) + + // The 'chooseDesktopMedia' api returns a streamId that + // identifies a media source in the constraints of 'getUserMedia' + // (see chromeMediaSourceId) + port.onMessage.addListener(result => { + navigator.mediaDevices.getUserMedia({ + video: { + mandatory: { + chromeMediaSource: "desktop", + chromeMediaSourceId: result.value.streamId + }, + } + }).then(stream => { + start(stream); + }, error => { + console.error(error); + }) + }) + + // Trigger the listener on the other side, + // we should see the picker dialog after this call. + port.postMessage({method: "chooseDesktopMedia"}) + } + + function stop() { + if (video.srcObject) + for (const track of video.srcObject.getTracks()) + track.stop() + video.srcObject = null; + video.setAttribute("style", "background-color: black;"); + } + + function start(stream) { + video.srcObject = stream; + video.play(); + } + + </script> +</html> diff --git a/tests/manual/widgets/webrtc/main.cpp b/tests/manual/widgets/webrtc/main.cpp new file mode 100644 index 000000000..b5718159c --- /dev/null +++ b/tests/manual/widgets/webrtc/main.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause + +#include <QApplication> +#include <QByteArray> +#include <QDialog> +#include <QFile> +#include <QHttpServer> +#include <QListView> +#include <QMessageBox> +#include <QWebEnginePage> +#include <QWebEngineProfile> +#include <QWebEngineSettings> +#include <QWebEngineView> + +#include "ui_mediaPicker.h" +#include <QWebEngineDesktopMediaRequest> + +// Test the screen/window selection and capturing APIs using QWebEngineDesktopMediaRequest, +// getDisplayMedia (js) and chooseDesktopMedia (hangouts) + +// Note: Wayland compositors require Pipewire support in QWE + +class Page : public QWebEnginePage +{ + Q_OBJECT + +public: + Page(QWebEngineProfile *profile, QObject *parent = nullptr); +private slots: + void handlePermissionRequest(const QUrl &origin, Feature feature); + void handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request); +}; + +Page::Page(QWebEngineProfile *profile, QObject *parent) : QWebEnginePage(profile, parent) +{ + settings()->setAttribute(QWebEngineSettings::ScreenCaptureEnabled, true); + connect(this, &QWebEnginePage::permissionRequested, this, + &Page::handlePermissionRequest); + connect(this, &QWebEnginePage::desktopMediaRequested, this, &Page::handleDesktopMediaRequest); +} + +void Page::handlePermissionRequest(QWebEnginePermission permission) +{ + if (QMessageBox::question(QApplication::activeWindow(), tr("Permission request"), + tr("allow access?")) + == QMessageBox::Yes) + permission.grant(); + else + permission.deny(); +} + +void Page::handleDesktopMediaRequest(const QWebEngineDesktopMediaRequest &request) +{ + Ui::MediaPickerDialog mediaPickerDialog; + QDialog dialog; + dialog.setModal(true); + mediaPickerDialog.setupUi(&dialog); + + auto *screensView = mediaPickerDialog.screensView; + auto *windowsView = mediaPickerDialog.windowsView; + auto *screensModel = request.screensModel(); + auto *windowsModel = request.windowsModel(); + + screensView->setModel(screensModel); + windowsView->setModel(windowsModel); + + if (dialog.exec() == QDialog::Accepted) { + if (mediaPickerDialog.tabWidget->currentIndex() == 0) + request.selectWindow(windowsView->selectionModel()->selectedIndexes().first()); + else + request.selectScreen(screensView->selectionModel()->selectedIndexes().first()); + } else { + request.cancel(); + } +} + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + QHttpServer server; + + QFile file(":index.html"); + + if (!file.open(QIODeviceBase::ReadOnly)) { + qWarning("failed to open file!"); + return 0; + } + + QByteArray data = file.readAll(); + if (data.isEmpty()) { + qWarning("failed to read file!"); + return 0; + } + + server.route("/index.html", [data]() { + return data; + }); + + server.listen(QHostAddress::Any, 3000); + + QWebEngineView view; + Page *page = new Page(QWebEngineProfile::defaultProfile(), &view); + view.setPage(page); + view.resize(1024, 750); + view.setUrl(QUrl("http://localhost:3000/index.html")); + view.show(); + return app.exec(); +} + +#include "main.moc" diff --git a/tests/manual/widgets/webrtc/mediaPicker.ui b/tests/manual/widgets/webrtc/mediaPicker.ui new file mode 100644 index 000000000..8bfab3f9b --- /dev/null +++ b/tests/manual/widgets/webrtc/mediaPicker.ui @@ -0,0 +1,118 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>MediaPickerDialog</class> + <widget class="QDialog" name="MediaPickerDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>500</width> + <height>400</height> + </rect> + </property> + <property name="windowTitle"> + <string>Choose what to share</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTabWidget" name="tabWidget"> + <property name="styleSheet"> + <string notr="true"/> + </property> + <property name="currentIndex"> + <number>0</number> + </property> + <widget class="QWidget" name="windows"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <property name="styleSheet"> + <string notr="true"/> + </property> + <attribute name="title"> + <string>Windows</string> + </attribute> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QListView" name="windowsView"/> + </item> + </layout> + </widget> + <widget class="QWidget" name="screens"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="contextMenuPolicy"> + <enum>Qt::NoContextMenu</enum> + </property> + <attribute name="title"> + <string>Screens</string> + </attribute> + <layout class="QVBoxLayout" name="verticalLayout_3"> + <item> + <widget class="QListView" name="screensView"/> + </item> + </layout> + </widget> + </widget> + </item> + <item> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>MediaPickerDialog</receiver> + <slot>accept()</slot> + <hints> + <hint type="sourcelabel"> + <x>248</x> + <y>254</y> + </hint> + <hint type="destinationlabel"> + <x>157</x> + <y>274</y> + </hint> + </hints> + </connection> + <connection> + <sender>buttonBox</sender> + <signal>rejected()</signal> + <receiver>MediaPickerDialog</receiver> + <slot>reject()</slot> + <hints> + <hint type="sourcelabel"> + <x>316</x> + <y>260</y> + </hint> + <hint type="destinationlabel"> + <x>286</x> + <y>274</y> + </hint> + </hints> + </connection> + </connections> +</ui> diff --git a/tests/manual/widgets/webrtc/qrc.qrc b/tests/manual/widgets/webrtc/qrc.qrc new file mode 100644 index 000000000..c3322b454 --- /dev/null +++ b/tests/manual/widgets/webrtc/qrc.qrc @@ -0,0 +1,5 @@ +<RCC> + <qresource prefix="/"> + <file>index.html</file> + </qresource> +</RCC> diff --git a/tests/manual/widgets/widgets.pro b/tests/manual/widgets/widgets.pro deleted file mode 100644 index 34e88f0e3..000000000 --- a/tests/manual/widgets/widgets.pro +++ /dev/null @@ -1,5 +0,0 @@ -TEMPLATE= subdirs - -SUBDIRS += \ - inputmethods \ - webgl |