From faba1a4b4ef82fe6f31c0247942671349bb07a7d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 10 Jun 2016 01:23:57 -0700 Subject: [PATCH] Fix UI elements triggered by WebViews --- .../QtWebEngine/UIDelegates/AlertDialog.qml | 25 +++++++ .../QtWebEngine/UIDelegates/ConfirmDialog.qml | 31 +++++++++ .../QtWebEngine/UIDelegates/FilePicker.qml | 39 +++++++++++ .../QtWebEngine/UIDelegates/Menu.qml | 68 +++++++++++++++++++ .../QtWebEngine/UIDelegates/MenuItem.qml | 39 +++++++++++ .../QtWebEngine/UIDelegates/MenuSeparator.qml | 6 ++ .../QtWebEngine/UIDelegates/MessageBubble.qml | 4 ++ .../QtWebEngine/UIDelegates/PromptDialog.qml | 42 ++++++++++++ .../resources/QtWebEngine/UIDelegates/qmldir | 8 +++ interface/resources/qml/Browser.qml | 1 + libraries/gl/src/gl/OffscreenQmlSurface.cpp | 5 ++ libraries/ui/src/FileDialogHelper.cpp | 7 ++ libraries/ui/src/FileDialogHelper.h | 1 + tests/ui/qml/main.qml | 9 ++- tests/ui/qmlscratch.pro | 2 + tests/ui/src/main.cpp | 42 ++++++++++-- 16 files changed, 320 insertions(+), 9 deletions(-) create mode 100644 interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/FilePicker.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/Menu.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MenuItem.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml create mode 100644 interface/resources/QtWebEngine/UIDelegates/qmldir diff --git a/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml new file mode 100644 index 0000000000..e6dc03fa55 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/AlertDialog.qml @@ -0,0 +1,25 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.0 + +import "../../qml/dialogs" + +QtObject { + id: root + signal accepted; + property var text; + + property var messageDialogBuilder: Component { MessageDialog { } } + + function open() { + console.log("prompt text " + text) + var dialog = messageDialogBuilder.createObject(desktop, { + text: root.text + }); + + dialog.selected.connect(function(button){ + accepted(); + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml new file mode 100644 index 0000000000..97d83f4304 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/ConfirmDialog.qml @@ -0,0 +1,31 @@ +import QtQuick 2.4 + +import QtQuick.Dialogs 1.1 as OriginalDialogs + +import "../../qml/dialogs" + +QtObject { + id: root + signal accepted; + signal rejected; + property var text; + + property var messageDialogBuilder: Component { MessageDialog { } } + + function open() { + var dialog = messageDialogBuilder.createObject(desktop, { + text: root.text, + icon: OriginalDialogs.StandardIcon.Question, + buttons: OriginalDialogs.StandardButton.Ok | OriginalDialogs.StandardButton.Cancel + }); + + dialog.selected.connect(function(button){ + if (button === OriginalDialogs.StandardButton.Ok) { + accepted() + } else { + rejected(); + } + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml new file mode 100644 index 0000000000..cb6552b075 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/FilePicker.qml @@ -0,0 +1,39 @@ +import QtQuick 2.4 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.4 + +import "../../qml/dialogs" + +QtObject { + id: root + signal filesSelected(var fileList); + signal rejected(); + property var text; + property url fileUrl; + property var fileUrls; + property url folder; + property var nameFilters; + property bool selectExisting; + property bool selectFolder; + property bool selectMultiple; + property string selectedNameFilter; + property string title; + + property var fileDialogBuilder: Component { FileDialog { } } + + function open() { + var foo = root; + var dialog = fileDialogBuilder.createObject(desktop, { + }); + + dialog.canceled.connect(function(){ + root.filesSelected([]); + dialog.destroy(); + }); + + dialog.selectedFile.connect(function(file){ + root.filesSelected(fileDialogHelper.urlToList(file)); + }); + } +} + diff --git a/interface/resources/QtWebEngine/UIDelegates/Menu.qml b/interface/resources/QtWebEngine/UIDelegates/Menu.qml new file mode 100644 index 0000000000..21c5e71394 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/Menu.qml @@ -0,0 +1,68 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Controls + +import "../../qml/menus" +import "../../qml/controls-uit" +import "../../qml/styles-uit" + +Item { + id: menu + HifiConstants { id: hifi } + signal done() + implicitHeight: column.height + implicitWidth: column.width + + Rectangle { + id: background + anchors { + fill: parent + margins: -16 + } + radius: hifi.dimensions.borderRadius + border.width: hifi.dimensions.borderWidth + border.color: hifi.colors.lightGrayText80 + color: hifi.colors.faintGray80 + } + + MouseArea { + id: closer + width: 8192 + height: 8192 + x: -4096 + y: -4096 + propagateComposedEvents: true + acceptedButtons: "AllButtons" + onClicked: { + menu.done(); + mouse.accepted = false; + } + } + + Column { + id: column + } + + function popup() { + var position = Reticle.position; + var localPosition = menu.parent.mapFromItem(desktop, position.x, position.y); + x = localPosition.x + y = localPosition.y + console.log("Popup at " + x + " x " + y) + var moveChildren = []; + for (var i = 0; i < children.length; ++i) { + var child = children[i]; + if (child.objectName !== "MenuItem") { + continue; + } + moveChildren.push(child); + } + + for (i = 0; i < moveChildren.length; ++i) { + child = moveChildren[i]; + child.parent = column; + child.menu = menu + } + } + +} + diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml new file mode 100644 index 0000000000..561a8926e1 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MenuItem.qml @@ -0,0 +1,39 @@ + +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Controls + +import "../../qml/controls-uit" +import "../../qml/styles-uit" + +Item { + id: root + objectName: "MenuItem" + + property alias text: label.text + property var menu; + property var shortcut; + signal triggered(); + + HifiConstants { id: hifi } + + implicitHeight: 2 * label.implicitHeight + implicitWidth: 2 * hifi.dimensions.menuPadding.x + label.width + + RalewaySemiBold { + id: label + size: hifi.fontSizes.rootMenu + anchors.left: parent.left + anchors.leftMargin: hifi.dimensions.menuPadding.x + verticalAlignment: Text.AlignVCenter + color: enabled ? hifi.colors.baseGrayShadow : hifi.colors.baseGrayShadow50 + enabled: root.enabled + } + + MouseArea { + anchors.fill: parent + onClicked: { + root.triggered(); + menu.done(); + } + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml b/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml new file mode 100644 index 0000000000..95d3b0a97b --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MenuSeparator.qml @@ -0,0 +1,6 @@ +import QtQuick 2.5 + +Item { + width: 100 + height: 20 +} diff --git a/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml b/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml new file mode 100644 index 0000000000..1a45563bf7 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/MessageBubble.qml @@ -0,0 +1,4 @@ +import QtQuick 2.5 + +Item { +} diff --git a/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml new file mode 100644 index 0000000000..01d3262bc0 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/PromptDialog.qml @@ -0,0 +1,42 @@ +import QtQuick 2.5 +import QtQuick.Controls 1.4 +import QtQuick.Layouts 1.0 + +import "../../qml/controls-uit" +import "../../qml/styles-uit" +import "../../qml/dialogs" + +QtObject { + id: root + signal input(string text); + signal accepted; + signal rejected; + signal closing(var close) + + property var titleWidth; + property var text; + property var prompt; + + property var inputDialogBuilder: Component { QueryDialog { } } + + function open() { + console.log("prompt text " + text) + console.log("prompt prompt " + prompt) + + var dialog = inputDialogBuilder.createObject(desktop, { + label: root.text, + current: root.prompt + }); + + dialog.selected.connect(function(result){ + root.input(dialog.result) + root.accepted(); + dialog.destroy(); + }); + + dialog.canceled.connect(function(){ + root.rejected(); + dialog.destroy(); + }); + } +} diff --git a/interface/resources/QtWebEngine/UIDelegates/qmldir b/interface/resources/QtWebEngine/UIDelegates/qmldir new file mode 100644 index 0000000000..69ebe1bad7 --- /dev/null +++ b/interface/resources/QtWebEngine/UIDelegates/qmldir @@ -0,0 +1,8 @@ +module QtWebEngine.UIDelegates +AlertDialog 1.0 AlertDialog.qml +ConfirmDialog 1.0 ConfirmDialog.qml +FilePicker 1.0 FilePicker.qml +PromptDialog 1.0 PromptDialog.qml +Menu 1.0 Menu.qml +MenuItem 1.0 MenuItem.qml +MenuSeparator 1.0 MenuSeparator.qml diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index e9b99331c7..ff8dd0987c 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -16,6 +16,7 @@ ScrollingWindow { destroyOnHidden: true width: 800 height: 600 + property alias url: webview.url property alias webView: webview x: 100 y: 100 diff --git a/libraries/gl/src/gl/OffscreenQmlSurface.cpp b/libraries/gl/src/gl/OffscreenQmlSurface.cpp index b4a9080ee8..8c167fafdc 100644 --- a/libraries/gl/src/gl/OffscreenQmlSurface.cpp +++ b/libraries/gl/src/gl/OffscreenQmlSurface.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include "OffscreenGLCanvas.h" #include "GLEscrow.h" @@ -400,6 +401,10 @@ void OffscreenQmlSurface::create(QOpenGLContext* shareContext) { // Create a QML engine. _qmlEngine = new QQmlEngine; + + auto importList = _qmlEngine->importPathList(); + importList.insert(importList.begin(), PathUtils::resourcesPath()); + _qmlEngine->setImportPathList(importList); if (!_qmlEngine->incubationController()) { _qmlEngine->setIncubationController(_renderer->_quickWindow->incubationController()); } diff --git a/libraries/ui/src/FileDialogHelper.cpp b/libraries/ui/src/FileDialogHelper.cpp index c9a8953f01..3a12e054df 100644 --- a/libraries/ui/src/FileDialogHelper.cpp +++ b/libraries/ui/src/FileDialogHelper.cpp @@ -108,3 +108,10 @@ QStringList FileDialogHelper::drives() { void FileDialogHelper::openDirectory(const QString& path) { QDesktopServices::openUrl(path); } + +QList FileDialogHelper::urlToList(const QUrl& url) { + QList results; + results.push_back(url); + return results; +} + diff --git a/libraries/ui/src/FileDialogHelper.h b/libraries/ui/src/FileDialogHelper.h index 832a5a6951..6058f8f7bb 100644 --- a/libraries/ui/src/FileDialogHelper.h +++ b/libraries/ui/src/FileDialogHelper.h @@ -58,6 +58,7 @@ public: Q_INVOKABLE bool validFolder(const QString& path); Q_INVOKABLE QUrl pathToUrl(const QString& path); Q_INVOKABLE QUrl saveHelper(const QString& saveText, const QUrl& currentFolder, const QStringList& selectionFilters); + Q_INVOKABLE QList urlToList(const QUrl& url); Q_INVOKABLE void openDirectory(const QString& path); }; diff --git a/tests/ui/qml/main.qml b/tests/ui/qml/main.qml index 8ca9399b74..607bd624a1 100644 --- a/tests/ui/qml/main.qml +++ b/tests/ui/qml/main.qml @@ -13,6 +13,7 @@ import "../../../interface/resources/qml/styles-uit" ApplicationWindow { id: appWindow + objectName: "MainWindow" visible: true width: 1280 height: 800 @@ -93,9 +94,6 @@ ApplicationWindow { onClicked: testButtons.lastButton.visible = !testButtons.lastButton.visible } - - - // Error alerts /* Button { @@ -350,6 +348,11 @@ ApplicationWindow { } */ + Browser { + url: "http://s3.amazonaws.com/DreamingContent/testUiDelegates.html" + } + + Window { id: blue closable: true diff --git a/tests/ui/qmlscratch.pro b/tests/ui/qmlscratch.pro index 151893de2f..5c9b91ee52 100644 --- a/tests/ui/qmlscratch.pro +++ b/tests/ui/qmlscratch.pro @@ -16,6 +16,8 @@ QML_IMPORT_PATH = ../../interface/resources/qml DISTFILES += \ qml/*.qml \ + ../../interface/resources/QtWebEngine/UIDelegates/original/*.qml \ + ../../interface/resources/QtWebEngine/UIDelegates/*.qml \ ../../interface/resources/qml/*.qml \ ../../interface/resources/qml/controls/*.qml \ ../../interface/resources/qml/controls-uit/*.qml \ diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index e3cf37ba04..312b5f3823 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -33,6 +33,28 @@ protected: const QString _name; }; +class Reticle : public QObject { + Q_OBJECT + Q_PROPERTY(QPoint position READ getPosition CONSTANT) +public: + + Reticle(QObject* parent) : QObject(parent) { + } + + QPoint getPosition() { + if (!_window) { + return QPoint(0, 0); + } + return _window->mapFromGlobal(QCursor::pos()); + } + + void setWindow(QWindow* window) { + _window = window; + } + +private: + QWindow* _window{nullptr}; +}; QString getRelativeDir(const QString& relativePath = ".") { QDir path(__FILE__); path.cdUp(); @@ -61,10 +83,8 @@ void setChild(QQmlApplicationEngine& engine, const char* name) { qWarning() << "Could not find object named " << name; } -void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath) { - QString resolvedPath = getRelativeDir("../qml"); - QUrl resolvedUrl = QUrl::fromLocalFile(resolvedPath); - resolvedPath = resolvedUrl.toString(); +void addImportPath(QQmlApplicationEngine& engine, const QString& relativePath, bool insert = false) { + QString resolvedPath = getRelativeDir(relativePath); engine.addImportPath(resolvedPath); } @@ -79,8 +99,9 @@ int main(int argc, char *argv[]) { qmlRegisterType("Hifi", 1, 0, "Preference"); QQmlApplicationEngine engine; - addImportPath(engine, "../qml"); - addImportPath(engine, "../../../interface/resources/qml"); + addImportPath(engine, "qml"); + addImportPath(engine, "../../interface/resources/qml"); + addImportPath(engine, "../../interface/resources"); engine.load(QUrl(QStringLiteral("qml/Stubs.qml"))); setChild(engine, "offscreenFlags"); @@ -99,6 +120,15 @@ int main(int argc, char *argv[]) { //engine.load(QUrl(QStringLiteral("qrc:/qml/gallery/main.qml"))); engine.load(QUrl(QStringLiteral("qml/main.qml"))); + for (QObject* rootObject : engine.rootObjects()) { + if (rootObject->objectName() == "MainWindow") { + Reticle* reticle = new Reticle(rootObject); + reticle->setWindow((QWindow*)rootObject); + engine.rootContext()->setContextProperty("Reticle", reticle); + engine.rootContext()->setContextProperty("Window", rootObject); + break; + } + } return app.exec(); }