diff --git a/examples/tests/qmlTest.js b/examples/tests/qmlTest.js index f1aa59cc09..c891b6a1b7 100644 --- a/examples/tests/qmlTest.js +++ b/examples/tests/qmlTest.js @@ -8,33 +8,6 @@ qmlWindow = new OverlayWindow({ visible: true }); -//qmlWindow.eventBridge.webEventReceived.connect(function(data) { -// print("JS Side event received: " + data); -//}); -// -//var titles = ["A", "B", "C"]; -//var titleIndex = 0; -// -//Script.setInterval(function() { -// qmlWindow.eventBridge.emitScriptEvent("JS Event sent"); -// var size = qmlWindow.size; -// var position = qmlWindow.position; -// print("Window visible: " + qmlWindow.visible) -// if (qmlWindow.visible) { -// print("Window size: " + size.x + "x" + size.y) -// print("Window pos: " + position.x + "x" + position.y) -// qmlWindow.setVisible(false); -// } else { -// qmlWindow.setVisible(true); -// qmlWindow.setTitle(titles[titleIndex]); -// qmlWindow.setSize(320 + Math.random() * 100, 240 + Math.random() * 100); -// titleIndex += 1; -// titleIndex %= titles.length; -// } -//}, 2 * 1000); -// -//Script.setTimeout(function() { -// print("Closing script"); -// qmlWindow.close(); -// Script.stop(); -//}, 15 * 1000) +Script.setInterval(function() { + qmlWindow.raise(); +}, 2 * 1000); diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index d84ea306b8..6c94f9d254 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -16,6 +16,7 @@ import "styles" DialogContainer { id: root HifiConstants { id: hifi } + z: 1000 objectName: "AddressBarDialog" @@ -150,6 +151,17 @@ DialogContainer { addressLine.forceActiveFocus() } } + + Timer { + running: root.enabled + interval: 500 + repeat: true + onTriggered: { + if (root.enabled && !addressLine.activeFocus) { + addressLine.forceActiveFocus() + } + } + } onVisibleChanged: { if (!visible) { diff --git a/interface/resources/qml/Global.js b/interface/resources/qml/Global.js new file mode 100644 index 0000000000..d41dde3c61 --- /dev/null +++ b/interface/resources/qml/Global.js @@ -0,0 +1,165 @@ +var OFFSCREEN_ROOT_OBJECT_NAME = "desktopRoot" +var OFFSCREEN_WINDOW_OBJECT_NAME = "topLevelWindow" + +function findChild(item, name) { + for (var i = 0; i < item.children.length; ++i) { + if (item.children[i].objectName === name) { + return item.children[i]; + } + } + return null; +} + +function findParent(item, name) { + while (item) { + if (item.objectName === name) { + return item; + } + item = item.parent; + } + return null; +} + +function getDesktop(item) { + return findParent(item, OFFSCREEN_ROOT_OBJECT_NAME); +} + +function findRootMenu(item) { + item = getDesktop(item); + return item ? item.rootMenu : null; +} + + +function getTopLevelWindows(item) { + var desktop = getDesktop(item); + var currentWindows = []; + if (!desktop) { + console.log("Could not find desktop for " + item) + return currentWindows; + } + + for (var i = 0; i < desktop.children.length; ++i) { + var child = desktop.children[i]; + if (Global.OFFSCREEN_WINDOW_OBJECT_NAME === child.objectName) { + var windowId = child.toString(); + currentWindows.push(child) + } + } + return currentWindows; +} + + +function getDesktopWindow(item) { + item = findParent(item, OFFSCREEN_WINDOW_OBJECT_NAME); + return item; +} + +function closeWindow(item) { + item = findDialog(item); + if (item) { + item.enabled = false + } else { + console.warn("Could not find top level dialog") + } +} + +function findMenuChild(menu, childName) { + if (!menu) { + return null; + } + + if (menu.type !== 2) { + console.warn("Tried to find child of a non-menu"); + return null; + } + + var items = menu.items; + var count = items.length; + for (var i = 0; i < count; ++i) { + var child = items[i]; + var name; + switch (child.type) { + case 2: + name = child.title; + break; + case 1: + name = child.text; + break; + default: + break; + } + + if (name && name === childName) { + return child; + } + } +} + +function findMenu(rootMenu, path) { + if ('string' === typeof(path)) { + path = [ path ] + } + + var currentMenu = rootMenu; + for (var i = 0; currentMenu && i < path.length; ++i) { + currentMenu = findMenuChild(currentMenu, path[i]); + } + + return currentMenu; +} + +function findInRootMenu(item, path) { + return findMenu(findRootMenu(item), path); +} + + +function menuItemsToListModel(parent, items) { + var newListModel = Qt.createQmlObject('import QtQuick 2.5; ListModel {}', parent); + for (var i = 0; i < items.length; ++i) { + var item = items[i]; + switch (item.type) { + case 2: + newListModel.append({"type":item.type, "name": item.title, "item": item}) + break; + case 1: + newListModel.append({"type":item.type, "name": item.text, "item": item}) + break; + case 0: + newListModel.append({"type":item.type, "name": "-----", "item": item}) + break; + } + } + return newListModel; +} + +function raiseWindow(item) { + var targetWindow = getDesktopWindow(item); + if (!targetWindow) { + console.warn("Could not find top level window for " + item); + return; + } + + var desktop = getDesktop(targetWindow); + if (!desktop) { + //console.warn("Could not find desktop for window " + targetWindow); + return; + } + + var maxZ = 0; + var minZ = 100000; + var windows = desktop.windows; + windows.sort(function(a, b){ + return a.z - b.z; + }); + var lastZ = -1; + var lastTargetZ = -1; + for (var i = 0; i < windows.length; ++i) { + var window = windows[i]; + if (window.z > lastZ) { + lastZ = window.z; + ++lastTargetZ; + } + window.z = lastTargetZ; + } + targetWindow.z = lastTargetZ + 1; +} diff --git a/interface/resources/qml/QmlWindow.qml b/interface/resources/qml/QmlWindow.qml index 2d8bf08dc5..f8217371e7 100644 --- a/interface/resources/qml/QmlWindow.qml +++ b/interface/resources/qml/QmlWindow.qml @@ -5,6 +5,8 @@ import QtWebChannel 1.0 import QtWebSockets 1.0 import "qrc:///qtwebchannel/qwebchannel.js" as WebChannel +import "Global.js" as Global + import "controls" import "styles" @@ -23,6 +25,10 @@ VrDialog { contentImplicitWidth: clientArea.implicitWidth contentImplicitHeight: clientArea.implicitHeight property alias source: pageLoader.source + + function raiseWindow() { + Global.raiseWindow(root) + } Item { id: clientArea diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 14799f78c0..49262cb7db 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -2,16 +2,23 @@ import Hifi 1.0 import QtQuick 2.5 import QtQuick.Controls 1.4 +import "Global.js" as Global + // This is our primary 'window' object to which all dialogs and controls will // be childed. Root { id: root - objectName: "desktopRoot" - anchors.fill: parent + objectName: Global.OFFSCREEN_ROOT_OBJECT_NAME + anchors.fill: parent; + property var windows: []; property var rootMenu: Menu { objectName: "rootMenu" } + onChildrenChanged: { + windows = Global.getTopLevelWindows(root); + } + onParentChanged: { forceActiveFocus(); } diff --git a/interface/resources/qml/controls/VrDialog.qml b/interface/resources/qml/controls/VrDialog.qml index 411fdbbb0b..18cab04533 100644 --- a/interface/resources/qml/controls/VrDialog.qml +++ b/interface/resources/qml/controls/VrDialog.qml @@ -3,6 +3,8 @@ import QtQuick.Controls 1.2 import "." import "../styles" +import "../Global.js" as Global + /* * FIXME Need to create a client property here so that objects can be * placed in it without having to think about positioning within the outer @@ -45,9 +47,18 @@ DialogBase { // trigger making it visible. if (enabled) { visible = true; + if (root.parent) { + Global.raiseWindow(root); + } } } + onParentChanged: { + if (enabled && parent) { + Global.raiseWindow(root); + } + } + // The actual animator Behavior on opacity { NumberAnimation { @@ -60,6 +71,12 @@ DialogBase { onOpacityChanged: { visible = (opacity != 0.0); } + + Component.onCompleted: { + if (visible) { + Global.raiseWindow(root); + } + } // Some dialogs should be destroyed when they become invisible, // so handle that @@ -118,6 +135,7 @@ DialogBase { y: root.titleY width: root.titleWidth height: root.titleHeight + onClicked: Global.raiseWindow(root) drag { target: root diff --git a/libraries/ui/src/QmlWindowClass.cpp b/libraries/ui/src/QmlWindowClass.cpp index 0dafc84d29..269ef6d6c9 100644 --- a/libraries/ui/src/QmlWindowClass.cpp +++ b/libraries/ui/src/QmlWindowClass.cpp @@ -15,6 +15,8 @@ #include #include +#include +#include #include #include @@ -140,6 +142,7 @@ QScriptValue QmlWindowClass::internalConstructor(const QString& qmlSource, Q_ARG(std::function, [&](QQmlContext* context, QObject* object) { setupServer(); retVal = function(context, object); + context->engine()->setObjectOwnership(retVal->_qmlWindow, QQmlEngine::CppOwnership); registerObject(url.toLower(), retVal); if (!title.isEmpty()) { retVal->setTitle(title); @@ -273,7 +276,7 @@ void QmlWindowClass::hasClosed() { } void QmlWindowClass::raise() { - // FIXME + QMetaObject::invokeMethod(_qmlWindow, "raiseWindow", Qt::QueuedConnection); } #include "QmlWindowClass.moc"