diff --git a/BUILD_WIN.md b/BUILD_WIN.md index 39252ec4f2..80b8c35502 100644 --- a/BUILD_WIN.md +++ b/BUILD_WIN.md @@ -29,12 +29,12 @@ NOTE: Qt does not support 64-bit builds on Windows 7, so you must use the 32-bit * [Download the online installer](http://qt-project.org/downloads) * When it asks you to select components, ONLY select the following: - * Qt > Qt 5.3.2 > **msvc2013 32-bit OpenGL** + * Qt > Qt 5.4.1 > **msvc2013 32-bit OpenGL** -* [Download the offline installer](http://download.qt-project.org/official_releases/qt/5.3/5.3.2/qt-opensource-windows-x86-msvc2013_opengl-5.3.2.exe) +* [Download the offline installer](http://download.qt.io/official_releases/qt/5.4/5.4.1/qt-opensource-windows-x86-msvc2013_opengl-5.4.1.exe) Once Qt is installed, you need to manually configure the following: -* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.3.2\msvc2013_opengl\lib\cmake` directory. +* Set the QT_CMAKE_PREFIX_PATH environment variable to your `Qt\5.4.1\msvc2013_opengl\lib\cmake` directory. * You can set an environment variable from Control Panel > System > Advanced System Settings > Environment Variables > New ###External Libraries diff --git a/examples/utilities/tools/developerMenuItems.js b/examples/utilities/tools/developerMenuItems.js index 58b5149307..ace2b032e2 100644 --- a/examples/utilities/tools/developerMenuItems.js +++ b/examples/utilities/tools/developerMenuItems.js @@ -13,6 +13,7 @@ var createdRenderMenu = false; var createdGeneratedAudioMenu = false; +var createdStereoInputMenuItem = false; var DEVELOPER_MENU = "Developer"; @@ -28,6 +29,7 @@ var AUDIO_SOURCE_INJECT = "Generated Audio"; var AUDIO_SOURCE_MENU = AUDIO_MENU + " > Generated Audio Source"; var AUDIO_SOURCE_PINK_NOISE = "Pink Noise"; var AUDIO_SOURCE_SINE_440 = "Sine 440hz"; +var AUDIO_STEREO_INPUT = "Stereo Input"; function setupMenus() { @@ -78,6 +80,10 @@ function setupMenus() { Audio.selectPinkNoise(); createdGeneratedAudioMenu = true; } + if (!Menu.menuItemExists(AUDIO_MENU, AUDIO_STEREO_INPUT)) { + Menu.addMenuItem({ menuName: AUDIO_MENU, menuItemName: AUDIO_STEREO_INPUT, isCheckable: true, isChecked: false }); + createdStereoInputMenuItem = true; + } } Menu.menuItemEvent.connect(function (menuItem) { @@ -99,6 +105,8 @@ Menu.menuItemEvent.connect(function (menuItem) { } else if (menuItem == AUDIO_SOURCE_SINE_440 && !createdGeneratedAudioMenu) { Audio.selectSine440(); Menu.setIsOptionChecked(AUDIO_SOURCE_PINK_NOISE, false); + } else if (menuItem == AUDIO_STEREO_INPUT) { + Audio.setStereoInput(Menu.isOptionChecked(AUDIO_STEREO_INPUT)) } }); @@ -125,6 +133,10 @@ function scriptEnding() { Menu.removeMenuItem(AUDIO_MENU, AUDIO_SOURCE_INJECT); Menu.removeMenu(AUDIO_SOURCE_MENU); } + + if (createdStereoInputMenuItem) { + Menu.removeMenuItem(AUDIO_MENU, AUDIO_STEREO_INPUT); + } } setupMenus(); diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8a2bc3e2b4..e12452472a 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -4,12 +4,16 @@ import "controls" import "styles" Dialog { + id: root + HifiConstants { id: hifi } + title: "Go to..." objectName: "AddressBarDialog" - height: 128 - width: 512 + contentImplicitWidth: addressBarDialog.implicitWidth + contentImplicitHeight: addressBarDialog.implicitHeight destroyOnCloseButton: false + onVisibleChanged: { if (!visible) { reset(); @@ -21,6 +25,11 @@ Dialog { addressLine.forceActiveFocus(); } } + onParentChanged: { + if (enabled && visible) { + addressLine.forceActiveFocus(); + } + } function reset() { addressLine.text = "" @@ -29,26 +38,28 @@ Dialog { AddressBarDialog { id: addressBarDialog - // The client area - anchors.fill: parent - anchors.margins: parent.margins - anchors.topMargin: parent.topMargin + x: root.clientX + y: root.clientY + implicitWidth: 512 + implicitHeight: border.height + hifi.layout.spacing * 4 + Border { + id: border height: 64 anchors.left: parent.left - anchors.leftMargin: 0 + anchors.leftMargin: hifi.layout.spacing * 2 anchors.right: goButton.left - anchors.rightMargin: 8 + anchors.rightMargin: hifi.layout.spacing anchors.verticalCenter: parent.verticalCenter TextInput { id: addressLine anchors.fill: parent helperText: "domain, location, @user, /x,y,z" - anchors.margins: 8 + anchors.margins: hifi.layout.spacing onAccepted: { - event.accepted + event.accepted addressBarDialog.loadAddress(addressLine.text) } } @@ -59,7 +70,7 @@ Dialog { width: 32 height: 32 anchors.right: parent.right - anchors.rightMargin: 8 + anchors.rightMargin: hifi.layout.spacing * 2 source: "../images/address-bar-submit.svg" anchors.verticalCenter: parent.verticalCenter diff --git a/interface/resources/qml/HifiAction.qml b/interface/resources/qml/HifiAction.qml deleted file mode 100644 index 0cecff91d7..0000000000 --- a/interface/resources/qml/HifiAction.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 - -Action { - property string name - objectName: name + "HifiAction" - text: qsTr(name) - - signal triggeredByName(string name); - signal toggledByName(string name); - - onTriggered: { - triggeredByName(name); - } - - onToggled: { - toggledByName(name, checked); - } -} - diff --git a/interface/resources/qml/HifiMenu.qml b/interface/resources/qml/HifiMenu.qml deleted file mode 100644 index 1afa39c5d1..0000000000 --- a/interface/resources/qml/HifiMenu.qml +++ /dev/null @@ -1,272 +0,0 @@ -import Hifi 1.0 as Hifi -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Controls.Styles 1.3 -import "controls" -import "styles" - -Hifi.HifiMenu { - id: root - anchors.fill: parent - objectName: "HifiMenu" - enabled: false - opacity: 0.0 - property int animationDuration: 200 - HifiPalette { id: hifiPalette } - z: 10000 - - onEnabledChanged: { - if (enabled && columns.length == 0) { - pushColumn(rootMenu.items); - } - opacity = enabled ? 1.0 : 0.0 - if (enabled) { - forceActiveFocus() - } - } - - // The actual animator - Behavior on opacity { - NumberAnimation { - duration: root.animationDuration - easing.type: Easing.InOutBounce - } - } - - onOpacityChanged: { - visible = (opacity != 0.0); - } - - onVisibleChanged: { - if (!visible) reset(); - } - - - property var menu: Menu {} - property var models: [] - property var columns: [] - property var itemBuilder: Component { - Text { - SystemPalette { id: sp; colorGroup: SystemPalette.Active } - id: thisText - x: 32 - property var source - property var root - property var listViewIndex - property var listView - text: typedText() - height: implicitHeight - width: implicitWidth - color: source.enabled ? "black" : "gray" - - onImplicitWidthChanged: { - if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); - listView.recalculateSize(); - } - } - - FontAwesome { - visible: source.type == 1 && source.checkable - x: -32 - text: (source.type == 1 && source.checked) ? "\uF05D" : "\uF10C" - } - - FontAwesome { - visible: source.type == 2 - x: listView.width - 64 - text: "\uF0DA" - } - - - function typedText() { - switch(source.type) { - case 2: - return source.title; - case 1: - return source.text; - case 0: - return "-----" - } - } - - MouseArea { - id: mouseArea - acceptedButtons: Qt.LeftButton - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - width: listView.width - onClicked: { - listView.currentIndex = listViewIndex - parent.root.selectItem(parent.source); - } - } - } - } - - - property var menuBuilder: Component { - Border { - SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } - x: root.models.length == 1 ? - (root.width / 2 - width / 2) : - root.columns[root.models.length - 2].x + 60; - anchors.verticalCenter: parent.verticalCenter - border.color: hifiPalette.hifiBlue - color: sysPalette.window - - ListView { - spacing: 6 - property int outerMargin: 8 - property real minWidth: 0 - anchors.fill: parent - anchors.margins: outerMargin - id: listView - height: root.height - currentIndex: -1 - - onCountChanged: { - recalculateSize() - } - - function recalculateSize() { - var newHeight = 0 - var newWidth = minWidth; - for (var i = 0; i < children.length; ++i) { - var item = children[i]; - newHeight += item.height - } - parent.height = newHeight + outerMargin * 2; - parent.width = newWidth + outerMargin * 2 - } - - highlight: Rectangle { - width: listView.minWidth; height: 32 - color: sysPalette.highlight - y: (listView.currentItem) ? listView.currentItem.y : 0; - x: 32 - Behavior on y { - NumberAnimation { - duration: 100 - easing.type: Easing.InOutQuint - } - } - } - - - property int columnIndex: root.models.length - 1 - model: root.models[columnIndex] - delegate: Loader { - id: loader - sourceComponent: root.itemBuilder - Binding { - target: loader.item - property: "root" - value: root - when: loader.status == Loader.Ready - } - Binding { - target: loader.item - property: "source" - value: modelData - when: loader.status == Loader.Ready - } - Binding { - target: loader.item - property: "listViewIndex" - value: index - when: loader.status == Loader.Ready - } - Binding { - target: loader.item - property: "listView" - value: listView - when: loader.status == Loader.Ready - } - } - - } - - } - } - - - function lastColumn() { - return columns[root.columns.length - 1]; - } - - function pushColumn(items) { - models.push(items) - if (columns.length) { - var oldColumn = lastColumn(); - oldColumn.enabled = false; - oldColumn.opacity = 0.5; - } - var newColumn = menuBuilder.createObject(root); - columns.push(newColumn); - newColumn.forceActiveFocus(); - } - - function popColumn() { - if (columns.length > 0) { - var curColumn = columns.pop(); - console.log(curColumn); - curColumn.visible = false; - curColumn.destroy(); - models.pop(); - } - - if (columns.length == 0) { - enabled = false; - return; - } - - curColumn = lastColumn(); - curColumn.enabled = true; - curColumn.opacity = 1.0; - curColumn.forceActiveFocus(); - } - - function selectItem(source) { - switch (source.type) { - case 2: - pushColumn(source.items) - break; - case 1: - source.trigger() - enabled = false - break; - case 0: - break; - } - } - - function reset() { - while (columns.length > 0) { - popColumn(); - } - } - - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Escape: - root.popColumn() - event.accepted = true; - } - } - - MouseArea { - anchors.fill: parent - id: mouseArea - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { - if (mouse.button == Qt.RightButton) { - root.popColumn(); - } else { - root.enabled = false; - } - } - } -} diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index b3b926bbe3..5653dfc7a1 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -5,9 +5,8 @@ import "controls" import "styles" Dialog { + HifiConstants { id: hifi } title: "Login" - HifiPalette { id: hifiPalette } - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } objectName: "LoginDialog" height: 512 width: 384 @@ -61,9 +60,6 @@ Dialog { anchors.margins: 8 KeyNavigation.tab: password KeyNavigation.backtab: password - onAccepted: { - password.forceActiveFocus() - } } } @@ -79,13 +75,6 @@ Dialog { anchors.margins: 8 KeyNavigation.tab: username KeyNavigation.backtab: username - onAccepted: { - if (username.text == "") { - username.forceActiveFocus() - } else { - loginDialog.login(username.text, password.text) - } - } onFocusChanged: { if (password.focus) { password.selectAll() @@ -117,7 +106,7 @@ Dialog { width: 192 height: 64 anchors.horizontalCenter: parent.horizontalCenter - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue border.width: 0 radius: 10 @@ -160,7 +149,7 @@ Dialog { text:"Create Account" font.pointSize: 12 font.bold: true - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue MouseArea { anchors.fill: parent @@ -177,7 +166,7 @@ Dialog { verticalAlignment: Text.AlignVCenter font.pointSize: 12 text: "Recover Password" - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue MouseArea { anchors.fill: parent @@ -188,4 +177,22 @@ Dialog { } } } + Keys.onPressed: { + switch(event.key) { + case Qt.Key_Enter: + case Qt.Key_Return: + if (username.activeFocus) { + event.accepted = true + password.forceActiveFocus() + } else if (password.activeFocus) { + event.accepted = true + if (username.text == "") { + username.forceActiveFocus() + } else { + loginDialog.login(username.text, password.text) + } + } + break; + } + } } diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index 315af8c6c9..dd410b8070 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -3,73 +3,51 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 import "controls" +import "styles" Dialog { id: root - property real spacing: 8 - property real outerSpacing: 16 - + HifiConstants { id: hifi } + property real spacing: hifi.layout.spacing + property real outerSpacing: hifi.layout.spacing * 2 destroyOnCloseButton: true destroyOnInvisible: true - implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 - implicitWidth: Math.min(200, Math.max(mainText.implicitWidth, content.buttonsRowImplicitWidth) + outerSpacing * 2); + contentImplicitWidth: content.implicitWidth + contentImplicitHeight: content.implicitHeight - onImplicitHeightChanged: root.height = implicitHeight - onImplicitWidthChanged: root.width = implicitWidth - - SystemPalette { id: palette } - - function calculateImplicitWidth() { - if (buttons.visibleChildren.length < 2) - return; - var calcWidth = 0; - for (var i = 0; i < buttons.visibleChildren.length; ++i) { - calcWidth += Math.max(100, buttons.visibleChildren[i].implicitWidth) + root.spacing - } - content.buttonsRowImplicitWidth = outerSpacing + calcWidth + 48 - } - Component.onCompleted: { enabled = true } - - onEnabledChanged: { - if (enabled) { - root.forceActiveFocus(); + + onParentChanged: { + if (visible && enabled) { + forceActiveFocus(); } } Hifi.MessageDialog { id: content clip: true - anchors.fill: parent - anchors.topMargin: parent.topMargin + root.outerSpacing - anchors.leftMargin: parent.margins + root.outerSpacing - anchors.rightMargin: parent.margins + root.outerSpacing - anchors.bottomMargin: parent.margins + root.outerSpacing - implicitHeight: contentColumn.implicitHeight + outerSpacing * 2 - implicitWidth: Math.max(mainText.implicitWidth, buttonsRowImplicitWidth); - property real buttonsRowImplicitWidth: Screen.pixelDensity * 50 - onImplicitWidthChanged: root.width = implicitWidth + x: root.clientX + y: root.clientY + implicitHeight: contentColumn.implicitHeight + outerSpacing * 2 + implicitWidth: mainText.implicitWidth + outerSpacing * 2 Component.onCompleted: { root.title = title } - + onTitleChanged: { root.title = title } - + Column { + anchors.fill: parent + anchors.margins: 8 id: contentColumn spacing: root.outerSpacing - anchors { - top: parent.top - left: parent.left - right: parent.right - } Item { width: parent.width @@ -85,7 +63,7 @@ Dialog { horizontalAlignment: Text.AlignLeft color: iconColor() text: iconSymbol() - + function iconSymbol() { switch (content.icon) { case Hifi.MessageDialog.Information: @@ -264,7 +242,6 @@ Dialog { onClicked: content.click(StandardButton.Help) visible: content.standardButtons & StandardButton.Help } - onVisibleChildrenChanged: root.calculateImplicitWidth() } } @@ -321,6 +298,7 @@ Dialog { ] } + Keys.onPressed: { if (event.modifiers === Qt.ControlModifier) switch (event.key) { @@ -335,7 +313,7 @@ Dialog { case Qt.Key_Period: if (Qt.platform.os === "osx") { event.accepted = true - content.reject() + content.reject() } break } else switch (event.key) { @@ -347,7 +325,6 @@ Dialog { case Qt.Key_Enter: case Qt.Key_Return: - console.log("Accepting"); event.accepted = true content.accept() break diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 9b2e5af6b5..1b0f09558f 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -8,7 +8,7 @@ Root { anchors.fill: parent onParentChanged: { - forceActiveFocus(); + forceActiveFocus(); } } diff --git a/interface/resources/qml/TestMenu.qml b/interface/resources/qml/TestMenu.qml new file mode 100644 index 0000000000..5aff18b421 --- /dev/null +++ b/interface/resources/qml/TestMenu.qml @@ -0,0 +1,115 @@ +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import Hifi 1.0 + +// Currently for testing a pure QML replacement menu +Item { + Item { + objectName: "AllActions" + Action { + id: aboutApp + objectName: "HifiAction_" + MenuConstants.AboutApp + text: qsTr("About Interface") + } + + // + // File Menu + // + Action { + id: login + objectName: "HifiAction_" + MenuConstants.Login + text: qsTr("Login") + } + Action { + id: quit + objectName: "HifiAction_" + MenuConstants.Quit + text: qsTr("Quit") + //shortcut: StandardKey.Quit + shortcut: "Ctrl+Q" + } + + + // + // Edit menu + // + Action { + id: undo + text: "Undo" + shortcut: StandardKey.Undo + } + + Action { + id: redo + text: "Redo" + shortcut: StandardKey.Redo + } + + Action { + id: animations + objectName: "HifiAction_" + MenuConstants.Animations + text: qsTr("Animations...") + } + Action { + id: attachments + text: qsTr("Attachments...") + } + Action { + id: explode + text: qsTr("Explode on quit") + checkable: true + checked: true + } + Action { + id: freeze + text: qsTr("Freeze on quit") + checkable: true + checked: false + } + ExclusiveGroup { + Action { + id: visibleToEveryone + objectName: "HifiAction_" + MenuConstants.VisibleToEveryone + text: qsTr("Everyone") + checkable: true + checked: true + } + Action { + id: visibleToFriends + objectName: "HifiAction_" + MenuConstants.VisibleToFriends + text: qsTr("Friends") + checkable: true + } + Action { + id: visibleToNoOne + objectName: "HifiAction_" + MenuConstants.VisibleToNoOne + text: qsTr("No one") + checkable: true + } + } + } + + Menu { + objectName: "rootMenu"; + Menu { + title: "File" + MenuItem { action: login } + MenuItem { action: explode } + MenuItem { action: freeze } + MenuItem { action: quit } + } + Menu { + title: "Tools" + Menu { + title: "I Am Visible To" + MenuItem { action: visibleToEveryone } + MenuItem { action: visibleToFriends } + MenuItem { action: visibleToNoOne } + } + MenuItem { action: animations } + } + Menu { + title: "Help" + MenuItem { action: aboutApp } + } + } +} diff --git a/interface/resources/qml/TestRoot.qml b/interface/resources/qml/TestRoot.qml index cb53d34ce2..bd38c696bf 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -6,10 +6,10 @@ import QtQuick.Controls 1.3 import "controls" Root { - id: root + id: root anchors.fill: parent onParentChanged: { - forceActiveFocus(); + forceActiveFocus(); } Button { id: messageBox @@ -37,7 +37,7 @@ Root { } Keys.onPressed: { - console.log("Key press root") - } + console.log("Key press root") + } } diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml new file mode 100644 index 0000000000..d81d79aa0b --- /dev/null +++ b/interface/resources/qml/VrMenu.qml @@ -0,0 +1,326 @@ +import Hifi 1.0 as Hifi +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Styles 1.3 +import "controls" +import "styles" + +Hifi.VrMenu { + id: root + HifiConstants { id: hifi } + + anchors.fill: parent + + objectName: "VrMenu" + + enabled: false + opacity: 0.0 + + property int animationDuration: 200 + property var models: [] + property var columns: [] + + z: 10000 + + onEnabledChanged: { + if (enabled && columns.length == 0) { + pushColumn(rootMenu.items); + } + opacity = enabled ? 1.0 : 0.0 + if (enabled) { + forceActiveFocus() + } + } + + // The actual animator + Behavior on opacity { + NumberAnimation { + duration: root.animationDuration + easing.type: Easing.InOutBounce + } + } + + onOpacityChanged: { + visible = (opacity != 0.0); + } + + onVisibleChanged: { + if (!visible) reset(); + } + + property var menuBuilder: Component { + Border { + HifiConstants { id: hifi } + Component.onCompleted: { + menuDepth = root.models.length - 1 + if (menuDepth == 0) { + x = lastMousePosition.x - 20 + y = lastMousePosition.y - 20 + } else { + var lastColumn = root.columns[menuDepth - 1] + x = lastColumn.x + 64; + y = lastMousePosition.y - height / 2; + } + } + border.color: hifi.colors.hifiBlue + color: hifi.colors.window + property int menuDepth + implicitHeight: listView.implicitHeight + 16 + implicitWidth: listView.implicitWidth + 16 + + Column { + id: listView + property real minWidth: 0 + anchors { + top: parent.top + topMargin: 8 + left: parent.left + leftMargin: 8 + right: parent.right + rightMargin: 8 + } + + Repeater { + model: root.models[menuDepth] + delegate: Loader { + id: loader + sourceComponent: root.itemBuilder + Binding { + target: loader.item + property: "root" + value: root + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "source" + value: modelData + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "border" + value: listView.parent + when: loader.status == Loader.Ready + } + Binding { + target: loader.item + property: "listView" + value: listView + when: loader.status == Loader.Ready + } + } + } + } + } + } + + property var itemBuilder: Component { + Item { + property var source + property var root + property var listView + property var border + implicitHeight: row.implicitHeight + 4 + implicitWidth: row.implicitWidth + label.height + // FIXME uncommenting this line results in menus that have blank spots + // rather than having the correct size + // visible: source.visible + Row { + id: row + spacing: 2 + anchors { + top: parent.top + topMargin: 2 + } + Spacer { size: 4 } + FontAwesome { + id: check + verticalAlignment: Text.AlignVCenter + y: 2 + size: label.height + text: checkText() + color: label.color + function checkText() { + if (!source || source.type != 1 || !source.checkable) { + return ""; + } + + // FIXME this works for native QML menus but I don't think it will + // for proxied QML menus + if (source.exclusiveGroup) { + return source.checked ? "\uF05D" : "\uF10C" + } + return source.checked ? "\uF046" : "\uF096" + } + } + Text { + id: label + text: typedText() + color: source.enabled ? hifi.colors.text : hifi.colors.disabledText + enabled: source.enabled && source.visible + function typedText() { + if (source) { + switch(source.type) { + case 2: + return source.title; + case 1: + return source.text; + case 0: + return "-----" + } + } + return "" + } + } + } // row + + FontAwesome { + anchors { + top: row.top + } + id: tag + size: label.height + width: implicitWidth + visible: source.type == 2 + x: listView.width - width - 4 + text: "\uF0DA" + color: label.color + } + + MouseArea { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: tag.right + rightMargin: -4 + } + acceptedButtons: Qt.LeftButton + hoverEnabled: true + Rectangle { + id: highlight + visible: false + anchors.fill: parent + color: "#7f0e7077" + } + Timer { + id: timer + interval: 1000 + onTriggered: parent.select(); + } + onEntered: { + /* + * Uncomment below to have menus auto-popup + * + * FIXME if we enabled timer based menu popup, either the timer has + * to be very very short or after auto popup there has to be a small + * amount of time, or a test if the mouse has moved before a click + * will be accepted, otherwise it's too easy to accidently click on + * something immediately after the auto-popup appears underneath your + * cursor + * + */ + //if (source.type == 2 && enabled) { + // timer.start() + //} + highlight.visible = source.enabled + } + onExited: { + timer.stop() + highlight.visible = false + } + onClicked: { + select(); + } + function select() { + //timer.stop(); + var popped = false; + while (columns.length - 1 > listView.parent.menuDepth) { + popColumn(); + popped = true; + } + + if (!popped || source.type != 1) { + parent.root.selectItem(parent.source); + } + } + } + } + } + + function lastColumn() { + return columns[root.columns.length - 1]; + } + + function pushColumn(items) { + models.push(items) + if (columns.length) { + var oldColumn = lastColumn(); + //oldColumn.enabled = false + } + var newColumn = menuBuilder.createObject(root); + columns.push(newColumn); + newColumn.forceActiveFocus(); + } + + function popColumn() { + if (columns.length > 0) { + var curColumn = columns.pop(); + curColumn.visible = false; + curColumn.destroy(); + models.pop(); + } + + if (columns.length == 0) { + enabled = false; + return; + } + + curColumn = lastColumn(); + curColumn.enabled = true; + curColumn.opacity = 1.0; + curColumn.forceActiveFocus(); + } + + function selectItem(source) { + switch (source.type) { + case 2: + pushColumn(source.items) + break; + case 1: + source.trigger() + enabled = false + break; + case 0: + break; + } + } + + function reset() { + while (columns.length > 0) { + popColumn(); + } + } + + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Escape: + root.popColumn() + event.accepted = true; + } + } + + MouseArea { + anchors.fill: parent + id: mouseArea + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { + if (mouse.button == Qt.RightButton) { + root.popColumn(); + } else { + root.enabled = false; + } + } + } +} diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml index 215e0542f7..989d5b579c 100644 --- a/interface/resources/qml/controls/Button.qml +++ b/interface/resources/qml/controls/Button.qml @@ -5,6 +5,5 @@ import "." import "../styles" Original.Button { - style: ButtonStyle { - } + style: ButtonStyle { } } diff --git a/interface/resources/qml/controls/CheckBox.qml b/interface/resources/qml/controls/CheckBox.qml new file mode 100644 index 0000000000..fe836a0e89 --- /dev/null +++ b/interface/resources/qml/controls/CheckBox.qml @@ -0,0 +1,16 @@ +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 +import "../styles" +import "." +Original.CheckBox { + text: "Check Box" + style: CheckBoxStyle { + indicator: FontAwesome { + text: control.checked ? "\uf046" : "\uf096" + } + label: Text { + text: control.text + } + } + +} \ No newline at end of file diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index d722d5264a..46add1dc07 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -11,28 +11,21 @@ import "../styles" * Examine the QML ApplicationWindow.qml source for how it does this * */ -Item { +DialogBase { id: root - - HifiPalette { id: hifiPalette } - SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } + HifiConstants { id: hifi } + // FIXME better placement via a window manager x: parent ? parent.width / 2 - width / 2 : 0 y: parent ? parent.height / 2 - height / 2 : 0 - property int animationDuration: 400 property bool destroyOnInvisible: false property bool destroyOnCloseButton: true property bool resizable: false + + property int animationDuration: hifi.effects.fadeInDuration property int minX: 256 property int minY: 256 - property int topMargin: root.height - clientBorder.height + 8 - property int margins: 8 - property string title - property int titleSize: titleBorder.height + 12 - property string frameColor: hifiPalette.hifiBlue - property string backgroundColor: sysPalette.window - property string headerBackgroundColor: sysPalette.dark - clip: true + readonly property int topMargin: root.height - clientBorder.height + hifi.layout.spacing /* * Support for animating the dialog in and out. @@ -44,7 +37,8 @@ Item { // visibility, so that we can do animations in both directions. Because // visibility and enabled are boolean flags, they cannot be animated. So when // enabled is change, we modify a property that can be animated, like scale or - // opacity. + // opacity, and then when the target animation value is reached, we can + // modify the visibility onEnabledChanged: { scale = enabled ? 1.0 : 0.0 } @@ -57,13 +51,13 @@ Item { } } - // We remove any load the dialog might have on the QML by toggling it's - // visibility based on the state of the animated property + // Once we're scaled to 0, disable the dialog's visibility onScaleChanged: { visible = (scale != 0.0); } - - // Some dialogs should be destroyed when they become invisible, so handle that + + // Some dialogs should be destroyed when they become invisible, + // so handle that onVisibleChanged: { if (!visible && destroyOnInvisible) { destroy(); @@ -91,19 +85,21 @@ Item { MouseArea { id: sizeDrag + enabled: root.resizable property int startX property int startY anchors.right: parent.right anchors.bottom: parent.bottom width: 16 height: 16 + z: 1000 hoverEnabled: true onPressed: { startX = mouseX startY = mouseY } onPositionChanged: { - if (pressed && root.resizable) { + if (pressed) { root.deltaSize((mouseX - startX), (mouseY - startY)) startX = mouseX startY = mouseY @@ -111,83 +107,52 @@ Item { } } - /* - * Window decorations, with a title bar and frames - */ - Border { - id: windowBorder - anchors.fill: parent - border.color: root.frameColor - color: root.backgroundColor + MouseArea { + id: titleDrag + x: root.titleX + y: root.titleY + width: root.titleWidth + height: root.titleHeight - Border { - id: titleBorder - height: 48 + drag { + target: root + minimumX: 0 + minimumY: 0 + maximumX: root.parent ? root.parent.width - root.width : 0 + maximumY: root.parent ? root.parent.height - root.height : 0 + } + + Row { + id: windowControls + anchors.bottom: parent.bottom + anchors.top: parent.top anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - border.color: root.frameColor - color: root.headerBackgroundColor - - Text { - id: titleText - // FIXME move all constant colors to our own palette class HifiPalette - color: "white" - text: root.title - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - anchors.fill: parent - } - - MouseArea { - id: titleDrag - anchors.right: closeButton.left - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.top: parent.top - anchors.rightMargin: 4 - drag { - target: root - minimumX: 0 - minimumY: 0 - maximumX: root.parent ? root.parent.width - root.width : 0 - maximumY: root.parent ? root.parent.height - root.height : 0 - } - } - Image { - id: closeButton - x: 360 - height: 16 + anchors.rightMargin: hifi.layout.spacing + FontAwesome { + id: icon anchors.verticalCenter: parent.verticalCenter - width: 16 - anchors.right: parent.right - anchors.rightMargin: 12 - source: "../../styles/close.svg" + size: root.titleHeight - hifi.layout.spacing * 2 + color: "red" + text: "\uf00d" MouseArea { + anchors.margins: hifi.layout.spacing / 2 anchors.fill: parent onClicked: { root.close(); } } } - } // header border - - Border { - id: clientBorder - border.color: root.frameColor - // FIXME move all constant colors to our own palette class HifiPalette - color: "#00000000" - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: titleBorder.bottom - anchors.topMargin: -titleBorder.border.width - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - clip: true - } // client border - } // window border + } + } + Keys.onPressed: { + switch(event.key) { + case Qt.Key_W: + if (event.modifiers == Qt.ControlModifier) { + event.accepted = true + enabled = false + } + break; + } + } } diff --git a/interface/resources/qml/controls/DialogBase.qml b/interface/resources/qml/controls/DialogBase.qml new file mode 100644 index 0000000000..a6616fc731 --- /dev/null +++ b/interface/resources/qml/controls/DialogBase.qml @@ -0,0 +1,98 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import "." +import "../styles" + +Item { + id: root + HifiConstants { id: hifi } + implicitHeight: contentImplicitHeight + titleBorder.height + hifi.styles.borderWidth + implicitWidth: contentImplicitWidth + hifi.styles.borderWidth * 2 + property string title + property int titleSize: titleBorder.height + 12 + property string frameColor: hifi.colors.hifiBlue + property string backgroundColor: hifi.colors.dialogBackground + property bool active: false + property real contentImplicitWidth: 800 + property real contentImplicitHeight: 800 + + property alias titleBorder: titleBorder + readonly property alias titleX: titleBorder.x + readonly property alias titleY: titleBorder.y + readonly property alias titleWidth: titleBorder.width + readonly property alias titleHeight: titleBorder.height + + // readonly property real borderWidth: hifi.styles.borderWidth + readonly property real borderWidth: 0 + property alias clientBorder: clientBorder + readonly property real clientX: clientBorder.x + borderWidth + readonly property real clientY: clientBorder.y + borderWidth + readonly property real clientWidth: clientBorder.width - borderWidth * 2 + readonly property real clientHeight: clientBorder.height - borderWidth * 2 + + /* + * Window decorations, with a title bar and frames + */ + Border { + id: windowBorder + anchors.fill: parent + border.color: root.frameColor + border.width: 0 + color: "#00000000" + + Border { + id: titleBorder + height: hifi.layout.windowTitleHeight + anchors.right: parent.right + anchors.left: parent.left + border.color: root.frameColor + border.width: 0 + clip: true + color: root.active ? + hifi.colors.activeWindow.headerBackground : + hifi.colors.inactiveWindow.headerBackground + + + Text { + id: titleText + color: root.active ? + hifi.colors.activeWindow.headerText : + hifi.colors.inactiveWindow.headerText + text: root.title + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + anchors.fill: parent + } + } // header border + + // These two rectangles hide the curves between the title area + // and the client area + Rectangle { + y: titleBorder.height - titleBorder.radius + height: titleBorder.radius + width: titleBorder.width + color: titleBorder.color + visible: borderWidth == 0 + } + + Rectangle { + y: titleBorder.height + width: clientBorder.width + height: clientBorder.radius + color: clientBorder.color + } + + Border { + id: clientBorder + border.width: 0 + border.color: root.frameColor + color: root.backgroundColor + anchors.bottom: parent.bottom + anchors.top: titleBorder.bottom + anchors.topMargin: -titleBorder.border.width + anchors.right: parent.right + anchors.left: parent.left + } // client border + } // window border + +} diff --git a/interface/resources/qml/controls/FontAwesome.qml b/interface/resources/qml/controls/FontAwesome.qml index e975c0342b..50d7e96fb5 100644 --- a/interface/resources/qml/controls/FontAwesome.qml +++ b/interface/resources/qml/controls/FontAwesome.qml @@ -8,9 +8,9 @@ Text { property int size: 32 width: size height: size + font.pixelSize: size verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignLeft font.family: iconFont.name - font.pointSize: 18 } diff --git a/interface/resources/qml/controls/IconButton.qml b/interface/resources/qml/controls/IconButton.qml deleted file mode 100644 index 346865aacb..0000000000 --- a/interface/resources/qml/controls/IconButton.qml +++ /dev/null @@ -1,24 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.3 -import QtQuick.Window 2.2 -import QtQuick.Controls.Styles 1.3 - -Button { - text: "Text" - style: ButtonStyle { - background: Item { anchors.fill: parent } - label: Text { - id: icon - width: height - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - font.family: iconFont.name - font.pointSize: 18 - property alias unicode: icon.text - FontLoader { id: iconFont; source: "/fonts/fontawesome-webfont.ttf"; } - text: control.text - color: control.enabled ? "white" : "dimgray" - } - } -} - diff --git a/interface/resources/qml/controls/MenuButton.qml b/interface/resources/qml/controls/MenuButton.qml deleted file mode 100644 index 740995a199..0000000000 --- a/interface/resources/qml/controls/MenuButton.qml +++ /dev/null @@ -1,5 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.3 as Original -import "../styles" -import "../controls" - diff --git a/interface/resources/qml/controls/Slider.qml b/interface/resources/qml/controls/Slider.qml new file mode 100644 index 0000000000..ace20f1e68 --- /dev/null +++ b/interface/resources/qml/controls/Slider.qml @@ -0,0 +1,8 @@ +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 + +import "../styles" +import "." + +Original.Slider { +} diff --git a/interface/resources/qml/controls/Spacer.qml b/interface/resources/qml/controls/Spacer.qml new file mode 100644 index 0000000000..3c4f7456d6 --- /dev/null +++ b/interface/resources/qml/controls/Spacer.qml @@ -0,0 +1,11 @@ +import QtQuick 2.4 +import "../styles" + +Item { + id: root + HifiConstants { id: hifi } + property real size: hifi.layout.spacing + property real multiplier: 1.0 + height: size * multiplier + width: size * multiplier +} diff --git a/interface/resources/qml/controls/SpinBox.qml b/interface/resources/qml/controls/SpinBox.qml new file mode 100644 index 0000000000..7e44b9e4a3 --- /dev/null +++ b/interface/resources/qml/controls/SpinBox.qml @@ -0,0 +1,15 @@ + +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 + +import "../styles" +import "." + +Original.SpinBox { + style: SpinBoxStyle { + HifiConstants { id: hifi } + font.family: hifi.fonts.fontFamily + font.pointSize: hifi.fonts.fontSize + } + +} diff --git a/interface/resources/qml/controls/Text.qml b/interface/resources/qml/controls/Text.qml index a9c19e70b4..4f82f2d9e4 100644 --- a/interface/resources/qml/controls/Text.qml +++ b/interface/resources/qml/controls/Text.qml @@ -1,7 +1,9 @@ import QtQuick 2.3 as Original +import "../styles" Original.Text { - font.family: "Helvetica" - font.pointSize: 18 + HifiConstants { id: hifi } + font.family: hifi.fonts.fontFamily + font.pointSize: hifi.fonts.fontSize } diff --git a/interface/resources/qml/controls/TextAndSlider.qml b/interface/resources/qml/controls/TextAndSlider.qml new file mode 100644 index 0000000000..302c096878 --- /dev/null +++ b/interface/resources/qml/controls/TextAndSlider.qml @@ -0,0 +1,24 @@ +import QtQuick 2.3 as Original +import "../styles" +import "." + +Original.Item { + property alias text: label.text + property alias value: slider.value + + Text { + id: label + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + verticalAlignment: Original.Text.AlignVCenter + } + + Slider { + id: slider + width: 120 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } +} diff --git a/interface/resources/qml/controls/TextAndSpinBox.qml b/interface/resources/qml/controls/TextAndSpinBox.qml new file mode 100644 index 0000000000..a32a36a1f8 --- /dev/null +++ b/interface/resources/qml/controls/TextAndSpinBox.qml @@ -0,0 +1,26 @@ +import QtQuick 2.3 as Original +import "../styles" +import "." + +Original.Item { + property alias text: label.text + property alias value: spinBox.value + + Text { + id: label + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + verticalAlignment: Original.Text.AlignVCenter + text: "Minimum HMD FPS" + } + SpinBox { + id: spinBox + width: 120 + maximumValue: 240 + anchors.right: parent.right + anchors.top: parent.top + anchors.bottom: parent.bottom + } + +} diff --git a/interface/resources/qml/controls/TextArea.qml b/interface/resources/qml/controls/TextArea.qml index dfa177bcb6..a86e76620a 100644 --- a/interface/resources/qml/controls/TextArea.qml +++ b/interface/resources/qml/controls/TextArea.qml @@ -1,7 +1,9 @@ -import QtQuick 2.3 as Original +import QtQuick.Controls 2.3 as Original +import "../styles" Original.TextArea { - font.family: "Helvetica" - font.pointSize: 18 + HifiConstants { id: hifi } + font.family: hifi.fonts.fontFamily + font.pointSize: hifi.fonts.fontSize } diff --git a/interface/resources/qml/controls/TextEdit.qml b/interface/resources/qml/controls/TextEdit.qml index 28551bb171..b59b20a3d6 100644 --- a/interface/resources/qml/controls/TextEdit.qml +++ b/interface/resources/qml/controls/TextEdit.qml @@ -1,7 +1,9 @@ import QtQuick 2.3 as Original +import "../styles" Original.TextEdit { - font.family: "Helvetica" - font.pointSize: 18 + HifiConstants { id: hifi } + font.family: hifi.fonts.fontFamily + font.pointSize: hifi.fonts.fontSize } diff --git a/interface/resources/qml/controls/TextHeader.qml b/interface/resources/qml/controls/TextHeader.qml new file mode 100644 index 0000000000..9ce1da4ac2 --- /dev/null +++ b/interface/resources/qml/controls/TextHeader.qml @@ -0,0 +1,9 @@ +import "." +import "../styles" + +Text { + HifiConstants { id: hifi } + color: hifi.colors.hifiBlue + font.pointSize: hifi.fonts.headerPointSize + font.bold: true +} diff --git a/interface/resources/qml/controls/TextInput.qml b/interface/resources/qml/controls/TextInput.qml index 8ce3d85d81..d533c67bd6 100644 --- a/interface/resources/qml/controls/TextInput.qml +++ b/interface/resources/qml/controls/TextInput.qml @@ -1,34 +1,36 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 +import QtQuick 2.3 as Original +import "../styles" +import "." -TextInput { - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } +Original.TextInput { + id: root + HifiConstants { id: hifi } property string helperText - font.family: "Helvetica" - font.pointSize: 18 - width: 256 - height: 64 - color: myPalette.text + height: hifi.layout.rowHeight clip: true - verticalAlignment: TextInput.AlignVCenter + color: hifi.colors.text + verticalAlignment: Original.TextInput.AlignVCenter + font.family: hifi.fonts.fontFamily + font.pointSize: hifi.fonts.fontSize - onTextChanged: { - if (text == "") { - helperText.visible = true; - } else { - helperText.visible = false; - } +/* + Original.Rectangle { + // Render the rectangle as background + z: -1 + anchors.fill: parent + color: hifi.colors.inputBackground } - +*/ Text { - id: helperText anchors.fill: parent font.pointSize: parent.font.pointSize font.family: parent.font.family - verticalAlignment: TextInput.AlignVCenter - text: parent.helperText - color: myPalette.dark - clip: true + verticalAlignment: parent.verticalAlignment + horizontalAlignment: parent.horizontalAlignment + text: root.helperText + color: hifi.colors.hintText + visible: !root.text } } + diff --git a/interface/resources/qml/controls/TextInputAndButton.qml b/interface/resources/qml/controls/TextInputAndButton.qml new file mode 100644 index 0000000000..60e9001d72 --- /dev/null +++ b/interface/resources/qml/controls/TextInputAndButton.qml @@ -0,0 +1,40 @@ +import QtQuick 2.3 as Original +import "../styles" +import "." + +Original.Item { + id: root + HifiConstants { id: hifi } + height: hifi.layout.rowHeight + property string text + property string helperText + property string buttonText + property int buttonWidth: 0 + property alias input: input + property alias button: button + signal clicked() + + TextInput { + id: input + text: root.text + helperText: root.helperText + anchors.left: parent.left + anchors.right: button.left + anchors.rightMargin: 8 + anchors.bottom: parent.bottom + anchors.top: parent.top + } + + Button { + id: button + clip: true + width: root.buttonWidth ? root.buttonWidth : implicitWidth + text: root.buttonText + anchors.right: parent.right + anchors.bottom: parent.bottom + anchors.top: parent.top + onClicked: root.clicked() + } +} + + diff --git a/interface/resources/qml/styles/Border.qml b/interface/resources/qml/styles/Border.qml index 7d38e7d277..ae6bce9e39 100644 --- a/interface/resources/qml/styles/Border.qml +++ b/interface/resources/qml/styles/Border.qml @@ -1,11 +1,12 @@ import QtQuick 2.3 Rectangle { - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } - property int margin: 5 - color: myPalette.window - border.color: myPalette.dark - border.width: 5 - radius: border.width * 2 + HifiConstants { id: hifi } + implicitHeight: 64 + implicitWidth: 64 + color: hifi.colors.window + border.color: hifi.colors.hifiBlue + border.width: hifi.styles.borderWidth + radius: hifi.styles.borderRadius } diff --git a/interface/resources/qml/styles/ButtonStyle.qml b/interface/resources/qml/styles/ButtonStyle.qml index 8d866390a0..bcb167f4dc 100644 --- a/interface/resources/qml/styles/ButtonStyle.qml +++ b/interface/resources/qml/styles/ButtonStyle.qml @@ -4,7 +4,7 @@ import "." import "../controls" OriginalStyles.ButtonStyle { - Original.SystemPalette { id: myPalette; colorGroup: Original.SystemPalette.Active } + HifiConstants { id: hifi } padding { top: 8 left: 12 @@ -15,10 +15,9 @@ OriginalStyles.ButtonStyle { anchors.fill: parent } label: Text { - renderType: Original.Text.NativeRendering verticalAlignment: Original.Text.AlignVCenter horizontalAlignment: Original.Text.AlignHCenter text: control.text - color: control.enabled ? myPalette.text : myPalette.dark + color: control.enabled ? hifi.colors.text : hifi.colors.disabledText } } diff --git a/interface/resources/qml/styles/HifiConstants.qml b/interface/resources/qml/styles/HifiConstants.qml new file mode 100644 index 0000000000..d24e9ca9be --- /dev/null +++ b/interface/resources/qml/styles/HifiConstants.qml @@ -0,0 +1,61 @@ +import QtQuick 2.4 + +Item { + SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } + readonly property alias colors: colors + readonly property alias layout: layout + readonly property alias fonts: fonts + readonly property alias styles: styles + readonly property alias effects: effects + + Item { + id: colors + readonly property color hifiBlue: "#0e7077" + readonly property color window: sysPalette.window + readonly property color dialogBackground: sysPalette.window + //readonly property color dialogBackground: "#00000000" + readonly property color inputBackground: "white" + readonly property color background: sysPalette.dark + readonly property color text: sysPalette.text + readonly property color disabledText: "gray" + readonly property color hintText: sysPalette.dark + readonly property color light: sysPalette.light + readonly property alias activeWindow: activeWindow + readonly property alias inactiveWindow: inactiveWindow + QtObject { + id: activeWindow + readonly property color headerBackground: "white" + readonly property color headerText: "black" + } + QtObject { + id: inactiveWindow + readonly property color headerBackground: "gray" + readonly property color headerText: "black" + } + } + + QtObject { + id: fonts + readonly property real headerPointSize: 24 + readonly property string fontFamily: "Helvetica" + readonly property real fontSize: 18 + } + + QtObject { + id: layout + property int spacing: 8 + property int rowHeight: 40 + property int windowTitleHeight: 48 + } + + QtObject { + id: styles + readonly property int borderWidth: 5 + readonly property int borderRadius: borderWidth * 2 + } + + QtObject { + id: effects + readonly property int fadeInDuration: 400 + } +} diff --git a/interface/resources/qml/styles/HifiPalette.qml b/interface/resources/qml/styles/HifiPalette.qml index 46ef0ef14e..421fa2c75d 100644 --- a/interface/resources/qml/styles/HifiPalette.qml +++ b/interface/resources/qml/styles/HifiPalette.qml @@ -1,5 +1,11 @@ import QtQuick 2.4 -QtObject { +Item { property string hifiBlue: "#0e7077" -} \ No newline at end of file + property alias colors: colorsObj + + Item { + id: colorsObj + property string hifiRed: "red" + } +} diff --git a/interface/resources/qml/styles/IconButtonStyle.qml b/interface/resources/qml/styles/IconButtonStyle.qml index b341e5d6dd..812cd493b0 100644 --- a/interface/resources/qml/styles/IconButtonStyle.qml +++ b/interface/resources/qml/styles/IconButtonStyle.qml @@ -1,15 +1,10 @@ ButtonStyle { background: Item { anchors.fill: parent } - label: Text { + label: FontAwesome { id: icon - width: height - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - font.family: iconFont.name font.pointSize: 18 - property alias unicode: icon.text - FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; } + property alias unicode: text text: control.text - color: control.enabled ? "white" : "dimgray" + color: control.enabled ? hifi.colors.text : hifi.colors.disabledText } } diff --git a/interface/resources/qml/styles/MenuButtonStyle.qml b/interface/resources/qml/styles/MenuButtonStyle.qml deleted file mode 100644 index fd21e88d86..0000000000 --- a/interface/resources/qml/styles/MenuButtonStyle.qml +++ /dev/null @@ -1,22 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls.Styles 1.3 -import "../controls" -import "." - -ButtonStyle { - HifiPalette { id: hifiPalette } - padding { - top: 2 - left: 4 - right: 4 - bottom: 2 - } - background: Item {} - label: Text { - renderType: Text.NativeRendering - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text - color: control.enabled ? "yellow" : "brown" - } -} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1e3e6cfd9e..3f37067c75 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -63,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -356,9 +357,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : _bookmarks = new Bookmarks(); // Before setting up the menu - // call Menu getInstance static method to set up the menu - _window->setMenuBar(Menu::getInstance()); - _runningScriptsWidget = new RunningScriptsWidget(_window); // start the nodeThread so its event loop is running @@ -731,6 +729,10 @@ void Application::initializeGL() { qCDebug(interfaceapp, "Initialized Offscreen UI."); _glWidget->makeCurrent(); + // call Menu getInstance static method to set up the menu + // Needs to happen AFTER the QML UI initialization + _window->setMenuBar(Menu::getInstance()); + init(); qCDebug(interfaceapp, "init() complete."); @@ -766,6 +768,7 @@ void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); MessageDialog::registerType(); + VrMenu::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -774,6 +777,8 @@ void Application::initializeUi() { offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->load("Root.qml"); offscreenUi->load("RootMenu.qml"); + VrMenu::load(); + VrMenu::executeQueuedLambdas(); offscreenUi->setMouseTranslator([this](const QPointF& p){ if (OculusManager::isConnected()) { glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); @@ -1068,8 +1073,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) { return false; } -void Application::keyPressEvent(QKeyEvent* event) { +static bool _altPressed{ false }; +void Application::keyPressEvent(QKeyEvent* event) { + _altPressed = event->key() == Qt::Key_Alt; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1317,7 +1324,19 @@ void Application::keyPressEvent(QKeyEvent* event) { } } + +//#define VR_MENU_ONLY_IN_HMD + void Application::keyReleaseEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) { +#ifdef VR_MENU_ONLY_IN_HMD + if (OculusManager::isConnected()) { +#endif + VrMenu::toggle(); +#ifdef VR_MENU_ONLY_IN_HMD + } +#endif + } _keysPressed.remove(event->key()); @@ -1328,6 +1347,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { return; } + switch (event->key()) { case Qt::Key_E: case Qt::Key_PageUp: @@ -4447,3 +4467,7 @@ void Application::friendsWindowClosed() { delete _friendsWindow; _friendsWindow = NULL; } + +void Application::postLambdaEvent(std::function f) { + QCoreApplication::postEvent(this, new LambdaEvent(f)); +} diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 4f022623dc..896a50acca 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -84,7 +84,7 @@ void Bookmarks::persistToFile() { saveFile.write(data); } -void Bookmarks::setupMenus(Menu* menubar, QMenu* menu) { +void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { // Add menus/actions menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0, this, SLOT(bookmarkLocation())); @@ -192,7 +192,7 @@ void Bookmarks::enableMenuItems(bool enabled) { } void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { - QAction* teleportAction = new QAction(_bookmarksMenu); + QAction* teleportAction = _bookmarksMenu->newAction(); teleportAction->setData(address); connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index 59f9efb1b1..7ff9d48e8a 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -19,6 +19,7 @@ class QAction; class QMenu; class Menu; +class MenuWrapper; class Bookmarks: public QObject { Q_OBJECT @@ -26,7 +27,7 @@ class Bookmarks: public QObject { public: Bookmarks(); - void setupMenus(Menu* menubar, QMenu* menu); + void setupMenus(Menu* menubar, MenuWrapper* menu); private slots: void bookmarkLocation(); @@ -36,7 +37,7 @@ private slots: private: QVariantMap _bookmarks; // { name: address, ... } - QPointer _bookmarksMenu; + QPointer _bookmarksMenu; QPointer _deleteBookmarksAction; const QString BOOKMARKS_FILENAME = "bookmarks.json"; diff --git a/interface/src/Camera.cpp b/interface/src/Camera.cpp index e334fd7c65..6c6d165e7d 100644 --- a/interface/src/Camera.cpp +++ b/interface/src/Camera.cpp @@ -55,7 +55,6 @@ Camera::Camera() : _farClip(DEFAULT_FAR_CLIP), // default _hmdPosition(), _hmdRotation(), - _scale(1.0f), _isKeepLookingAt(false), _lookingAt(0.0f, 0.0f, 0.0f) { @@ -94,8 +93,8 @@ void Camera::setHmdRotation(const glm::quat& hmdRotation) { } float Camera::getFarClip() const { - return (_scale * _farClip < std::numeric_limits::max()) - ? _scale * _farClip + return (_farClip < std::numeric_limits::max()) + ? _farClip : std::numeric_limits::max() - 1; } diff --git a/interface/src/Camera.h b/interface/src/Camera.h index 7c6951b920..10572d3513 100644 --- a/interface/src/Camera.h +++ b/interface/src/Camera.h @@ -54,7 +54,6 @@ public: void setFarClip(float f); void setEyeOffsetPosition(const glm::vec3& p) { _eyeOffsetPosition = p; } void setEyeOffsetOrientation(const glm::quat& o) { _eyeOffsetOrientation = o; } - void setScale(const float s) { _scale = s; } glm::quat getRotation() const { return _rotation * _hmdRotation; } const glm::vec3& getHmdPosition() const { return _hmdPosition; } @@ -63,11 +62,10 @@ public: CameraMode getMode() const { return _mode; } float getFieldOfView() const { return _fieldOfView; } float getAspectRatio() const { return _aspectRatio; } - float getNearClip() const { return _scale * _nearClip; } + float getNearClip() const { return _nearClip; } float getFarClip() const; const glm::vec3& getEyeOffsetPosition() const { return _eyeOffsetPosition; } const glm::quat& getEyeOffsetOrientation() const { return _eyeOffsetOrientation; } - float getScale() const { return _scale; } public slots: QString getModeString() const; void setModeString(const QString& mode); @@ -107,7 +105,6 @@ private: glm::quat _rotation; glm::vec3 _hmdPosition; glm::quat _hmdRotation; - float _scale; bool _isKeepLookingAt; glm::vec3 _lookingAt; }; diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 43c63eb383..9b8504c03e 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include "Application.h" #include "AccountManager.h" @@ -52,7 +53,6 @@ Menu* Menu::getInstance() { if (!_instance) { qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - _instance = new Menu(); } @@ -62,8 +62,7 @@ Menu* Menu::getInstance() { } Menu::Menu() { - QMenu* fileMenu = addMenu("File"); - + MenuWrapper * fileMenu = addMenu("File"); #ifdef Q_OS_MAC addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); #endif @@ -113,7 +112,7 @@ Menu::Menu() { QAction::QuitRole); - QMenu* editMenu = addMenu("Edit"); + MenuWrapper* editMenu = addMenu("Edit"); QUndoStack* undoStack = qApp->getUndoStack(); QAction* undoAction = undoStack->createUndoAction(editMenu); @@ -136,7 +135,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, dialogsManager.data(), SLOT(editAnimations())); - QMenu* toolsMenu = addMenu("Tools"); + MenuWrapper* toolsMenu = addMenu("Tools"); addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, dialogsManager.data(), SLOT(showScriptEditor())); @@ -156,7 +155,7 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0, qApp, SLOT(showFriendsWindow())); - QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); + MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); { QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); auto discoverabilityManager = DependencyManager::get(); @@ -201,10 +200,10 @@ Menu::Menu() { addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, qApp, SLOT(packageModel())); - QMenu* avatarMenu = addMenu("Avatar"); + MenuWrapper* avatarMenu = addMenu("Avatar"); QObject* avatar = DependencyManager::get()->getMyAvatar(); - QMenu* avatarSizeMenu = avatarMenu->addMenu("Size"); + MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size"); addActionToQMenuAndActionHash(avatarSizeMenu, MenuOption::IncreaseAvatarSize, 0, // QML Qt::Key_Plus, @@ -233,7 +232,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); - QMenu* viewMenu = addMenu("View"); + MenuWrapper* viewMenu = addMenu("View"); addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Fullscreen, @@ -277,7 +276,7 @@ Menu::Menu() { SLOT(setEnable3DTVMode(bool))); - QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders"); + MenuWrapper* nodeBordersMenu = viewMenu->addMenu("Server Borders"); NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay(); addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, @@ -299,16 +298,16 @@ Menu::Menu() { dialogsManager.data(), SLOT(octreeStatsDetails())); - QMenu* developerMenu = addMenu("Developer"); + MenuWrapper* developerMenu = addMenu("Developer"); - QMenu* renderOptionsMenu = developerMenu->addMenu("Render"); + MenuWrapper* renderOptionsMenu = developerMenu->addMenu("Render"); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, 0, // QML Qt::SHIFT | Qt::Key_A, true); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion); addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges); - QMenu* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); + MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); ambientLightGroup->setExclusive(true); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true)); @@ -323,14 +322,14 @@ Menu::Menu() { ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - QMenu* shadowMenu = renderOptionsMenu->addMenu("Shadows"); + MenuWrapper* shadowMenu = renderOptionsMenu->addMenu("Shadows"); QActionGroup* shadowGroup = new QActionGroup(shadowMenu); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, "None", 0, true)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::SimpleShadows, 0, false)); shadowGroup->addAction(addCheckableActionToQMenuAndActionHash(shadowMenu, MenuOption::CascadedShadows, 0, false)); { - QMenu* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); + MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); QActionGroup* framerateGroup = new QActionGroup(framerateMenu); framerateGroup->setExclusive(true); framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); @@ -347,7 +346,7 @@ Menu::Menu() { } - QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); + MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); resolutionGroup->setExclusive(true); resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); @@ -367,9 +366,9 @@ Menu::Menu() { 0, // QML Qt::SHIFT | Qt::Key_L, dialogsManager.data(), SLOT(lodTools())); - QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar"); + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); - QMenu* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); + MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); { QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu); @@ -405,14 +404,14 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); - QMenu* handOptionsMenu = developerMenu->addMenu("Hands"); + MenuWrapper* handOptionsMenu = developerMenu->addMenu("Hands"); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlignForearmsWithWrists, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::AlternateIK, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHands, 0, true); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::DisplayHandTargets, 0, false); addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false); - QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); + MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); #ifdef __APPLE__ addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseEnabled, @@ -435,7 +434,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); - QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); + MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); #ifdef HAVE_RSSDK @@ -444,7 +443,7 @@ Menu::Menu() { RealSense::getInstance(), SLOT(loadRSSDKFile())); #endif - QMenu* networkMenu = developerMenu->addMenu("Network"); + MenuWrapper* networkMenu = developerMenu->addMenu("Network"); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableActivityLogger, @@ -457,8 +456,8 @@ Menu::Menu() { addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, dialogsManager.data(), SLOT(toggleDiskCacheEditor())); - QMenu* timingMenu = developerMenu->addMenu("Timing and Stats"); - QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); + MenuWrapper* timingMenu = developerMenu->addMenu("Timing and Stats"); + MenuWrapper* perfTimerMenu = timingMenu->addMenu("Performance Timer"); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); @@ -474,7 +473,7 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); auto audioIO = DependencyManager::get(); - QMenu* audioDebugMenu = developerMenu->addMenu("Audio"); + MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, 0, true, @@ -485,8 +484,6 @@ Menu::Menu() { audioIO.data(), SLOT(toggleServerEcho())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio, 0, false, audioIO.data(), SLOT(toggleLocalEcho())); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::StereoAudio, 0, false, - audioIO.data(), SLOT(toggleStereoInput())); addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::MuteAudio, Qt::CTRL | Qt::Key_M, false, @@ -500,7 +497,7 @@ Menu::Menu() { auto scope = DependencyManager::get(); - QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); + MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, Qt::CTRL | Qt::Key_P, false, scope.data(), @@ -550,11 +547,11 @@ Menu::Menu() { SLOT(toggleShowInjectedStreams())); - QMenu* physicsOptionsMenu = developerMenu->addMenu("Physics"); + MenuWrapper* physicsOptionsMenu = developerMenu->addMenu("Physics"); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowOwned); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowHulls); - QMenu* helpMenu = addMenu("Help"); + MenuWrapper* helpMenu = addMenu("Help"); addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); #ifndef Q_OS_MAC @@ -600,7 +597,7 @@ void Menu::scanMenu(QMenu& menu, settingsAction modifySetting, Settings& setting settings.endGroup(); } -void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) { +void Menu::addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation) { QAction* actionBefore = NULL; if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { actionBefore = destinationMenu->actions()[menuItemLocation]; @@ -620,7 +617,7 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } } -QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut, const QObject* receiver, @@ -657,7 +654,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, return action; } -QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, QAction* action, const QString& actionName, const QKeySequence& shortcut, @@ -692,7 +689,7 @@ QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, return action; } -QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, +QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut, const bool checked, @@ -708,7 +705,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, return action; } -void Menu::removeAction(QMenu* menu, const QString& actionName) { +void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { menu->removeAction(_actionHash.value(actionName)); _actionHash.remove(actionName); } @@ -747,7 +744,7 @@ QAction* Menu::getActionForOption(const QString& menuOption) { return _actionHash.value(menuOption); } -QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) { +QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { QList menuActions; if (menu) { menuActions = menu->actions(); @@ -756,6 +753,7 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) { } foreach (QAction* menuAction, menuActions) { + QString actionText = menuAction->text(); if (menuName == menuAction->text()) { return menuAction; } @@ -763,18 +761,18 @@ QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) { return NULL; } -QMenu* Menu::getSubMenuFromName(const QString& menuName, QMenu* menu) { +MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) { QAction* action = getActionFromName(menuName, menu); if (action) { - return action->menu(); + return MenuWrapper::fromMenu(action->menu()); } return NULL; } -QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { +MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; - QMenu* menu = NULL; + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; foreach (QString menuTreePart, menuTree) { parent = menu; finalMenuPart = menuTreePart.trimmed(); @@ -786,10 +784,10 @@ QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { return parent; } -QMenu* Menu::getMenu(const QString& menuName) { +MenuWrapper* Menu::getMenu(const QString& menuName) { QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; - QMenu* menu = NULL; + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; int item = 0; foreach (QString menuTreePart, menuTree) { menu = getSubMenuFromName(menuTreePart.trimmed(), parent); @@ -804,19 +802,19 @@ QMenu* Menu::getMenu(const QString& menuName) { QAction* Menu::getMenuAction(const QString& menuName) { QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; + MenuWrapper* parent = NULL; QAction* action = NULL; foreach (QString menuTreePart, menuTree) { action = getActionFromName(menuTreePart.trimmed(), parent); if (!action) { break; } - parent = action->menu(); + parent = MenuWrapper::fromMenu(action->menu()); } return action; } -int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) { +int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) { int position = 0; foreach(QAction* action, menu->actions()) { if (action->text() == searchMenuItem) { @@ -827,7 +825,7 @@ int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) { return UNSPECIFIED_POSITION; // not found } -int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) { +int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) { QList menuActions = menu->actions(); if (requestedPosition > 1 && requestedPosition < menuActions.size()) { QAction* beforeRequested = menuActions[requestedPosition - 1]; @@ -839,15 +837,15 @@ int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) { } -QMenu* Menu::addMenu(const QString& menuName) { +MenuWrapper* Menu::addMenu(const QString& menuName) { QStringList menuTree = menuName.split(">"); - QMenu* addTo = NULL; - QMenu* menu = NULL; + MenuWrapper* addTo = NULL; + MenuWrapper* menu = NULL; foreach (QString menuTreePart, menuTree) { menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); if (!menu) { if (!addTo) { - menu = QMenuBar::addMenu(menuTreePart.trimmed()); + menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed())); } else { menu = addTo->addMenu(menuTreePart.trimmed()); } @@ -865,7 +863,7 @@ void Menu::removeMenu(const QString& menuName) { // only proceed if the menu actually exists if (action) { QString finalMenuPart; - QMenu* parent = getMenuParent(menuName, finalMenuPart); + MenuWrapper* parent = getMenuParent(menuName, finalMenuPart); if (parent) { parent->removeAction(action); } else { @@ -887,14 +885,14 @@ bool Menu::menuExists(const QString& menuName) { } void Menu::addSeparator(const QString& menuName, const QString& separatorName) { - QMenu* menuObj = getMenu(menuName); + MenuWrapper* menuObj = getMenu(menuName); if (menuObj) { addDisabledActionAndSeparator(menuObj, separatorName); } } void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { - QMenu* menu = getMenu(menuName); + MenuWrapper* menu = getMenu(menuName); bool separatorRemoved = false; if (menu) { int textAt = findPositionOfMenuItem(menu, separatorName); @@ -917,7 +915,7 @@ void Menu::removeSeparator(const QString& menuName, const QString& separatorName } void Menu::addMenuItem(const MenuItemProperties& properties) { - QMenu* menuObj = getMenu(properties.menuName); + MenuWrapper* menuObj = getMenu(properties.menuName); if (menuObj) { QShortcut* shortcut = NULL; if (!properties.shortcutKeySequence.isEmpty()) { @@ -958,7 +956,7 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { } void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { - QMenu* menuObj = getMenu(menu); + MenuWrapper* menuObj = getMenu(menu); if (menuObj) { removeAction(menuObj, menuitem); QMenuBar::repaint(); @@ -972,3 +970,63 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { } return false; }; + + +MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addMenu(menu); + }); + _backMap[menu] = this; +} + +QList MenuWrapper::actions() { + return _realMenu->actions(); +} + +MenuWrapper* MenuWrapper::addMenu(const QString& menuName) { + return new MenuWrapper(_realMenu->addMenu(menuName)); +} + +void MenuWrapper::setEnabled(bool enabled) { + _realMenu->setEnabled(enabled); +} + +void MenuWrapper::addSeparator() { + _realMenu->addSeparator(); +} + +void MenuWrapper::addAction(QAction* action) { + _realMenu->addAction(action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); +} + +QAction* MenuWrapper::addAction(const QString& menuName) { + QAction* action = _realMenu->addAction(menuName); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); + return action; +} + +QAction* MenuWrapper::addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut) { + QAction* action = _realMenu->addAction(menuName, receiver, member, shortcut); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); + return action; +} + +void MenuWrapper::removeAction(QAction* action) { + _realMenu->removeAction(action); +} + +void MenuWrapper::insertAction(QAction* before, QAction* action) { + _realMenu->insertAction(before, action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->insertAction(before, action); + }); +} + +QHash MenuWrapper::_backMap; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2c4e2b809f..ef40674164 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -25,6 +25,35 @@ class Settings; +class MenuWrapper : public QObject { +public: + QList actions(); + MenuWrapper* addMenu(const QString& menuName); + void setEnabled(bool enabled = true); + void addSeparator(); + void addAction(QAction* action); + + QAction* addAction(const QString& menuName); + void insertAction(QAction* before, QAction* menuName); + + QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0); + void removeAction(QAction* action); + + QAction* newAction() { + return new QAction(_realMenu); + } +private: + MenuWrapper(QMenu* menu); + + static MenuWrapper* fromMenu(QMenu* menu) { + return _backMap[menu]; + } + + QMenu* const _realMenu; + static QHash _backMap; + friend class Menu; +}; + class Menu : public QMenuBar { Q_OBJECT public: @@ -33,29 +62,29 @@ public: void loadSettings(); void saveSettings(); - QMenu* getMenu(const QString& menuName); + MenuWrapper* getMenu(const QString& menuName); void triggerOption(const QString& menuOption); QAction* getActionForOption(const QString& menuOption); - QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut = 0, const QObject* receiver = NULL, const char* member = NULL, QAction::MenuRole role = QAction::NoRole, int menuItemLocation = UNSPECIFIED_POSITION); - QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu, + QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, QAction* action, const QString& actionName = QString(), const QKeySequence& shortcut = 0, QAction::MenuRole role = QAction::NoRole, int menuItemLocation = UNSPECIFIED_POSITION); - void removeAction(QMenu* menu, const QString& actionName); + void removeAction(MenuWrapper* menu, const QString& actionName); public slots: - QMenu* addMenu(const QString& menuName); + MenuWrapper* addMenu(const QString& menuName); void removeMenu(const QString& menuName); bool menuExists(const QString& menuName); void addSeparator(const QString& menuName, const QString& separatorName); @@ -77,10 +106,10 @@ private: void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); /// helper method to have separators with labels that are also compatible with OS X - void addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, + void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, int menuItemLocation = UNSPECIFIED_POSITION); - QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, const QString& actionName, const QKeySequence& shortcut = 0, const bool checked = false, @@ -88,13 +117,13 @@ private: const char* member = NULL, int menuItemLocation = UNSPECIFIED_POSITION); - QAction* getActionFromName(const QString& menuName, QMenu* menu); - QMenu* getSubMenuFromName(const QString& menuName, QMenu* menu); - QMenu* getMenuParent(const QString& menuName, QString& finalMenuPart); + QAction* getActionFromName(const QString& menuName, MenuWrapper* menu); + MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu); + MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart); QAction* getMenuAction(const QString& menuName); - int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem); - int positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition); + int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem); + int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); QHash _actionHash; }; @@ -238,7 +267,6 @@ namespace MenuOption { const QString ShiftHipsForIdleAnimations = "Shift hips for idle animations"; const QString Stars = "Stars"; const QString Stats = "Stats"; - const QString StereoAudio = "Stereo Audio (disables spatial sound)"; const QString StopAllScripts = "Stop All Scripts"; const QString SuppressShortTimings = "Suppress Timings Less than 10ms"; const QString TestPing = "Test Ping"; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cd7bedc19e..557d630ebf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -166,7 +166,6 @@ void MyAvatar::simulate(float deltaTime) { if (_scale != _targetScale) { float scale = (1.0f - SMOOTHING_RATIO) * _scale + SMOOTHING_RATIO * _targetScale; setScale(scale); - Application::getInstance()->getCamera()->setScale(scale); } { @@ -673,8 +672,6 @@ void MyAvatar::loadData() { _leanScale = loadSetting(settings, "leanScale", 0.05f); _targetScale = loadSetting(settings, "scale", 1.0f); setScale(_scale); - Application::getInstance()->getCamera()->setScale(_scale); - // The old preferences only stored the face and skeleton URLs, we didn't track if the user wanted to use 1 or 2 urls // for their avatar, So we need to attempt to detect this old case and set our new preferences accordingly. If diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 6ed3e48274..bd2903863d 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -45,13 +45,19 @@ QScriptValue WindowScriptingInterface::hasFocus() { } void WindowScriptingInterface::setFocus() { - auto window = Application::getInstance()->getWindow(); - window->activateWindow(); - window->setFocus(); + // It's forbidden to call focus() from another thread. + Application::getInstance()->postLambdaEvent([] { + auto window = Application::getInstance()->getWindow(); + window->activateWindow(); + window->setFocus(); + }); } void WindowScriptingInterface::raiseMainWindow() { - Application::getInstance()->getWindow()->raise(); + // It's forbidden to call raise() from another thread. + Application::getInstance()->postLambdaEvent([] { + Application::getInstance()->getWindow()->raise(); + }); } void WindowScriptingInterface::setCursorVisible(bool visible) { diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp new file mode 100644 index 0000000000..b9c640054c --- /dev/null +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -0,0 +1,29 @@ +// +// MarketplaceDialog.cpp +// +// Created by Bradley Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "Application.h" +#include "MarketplaceDialog.h" +#include "DependencyManager.h" + +HIFI_QML_DEF(MarketplaceDialog) + + +MarketplaceDialog::MarketplaceDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { +} + +bool MarketplaceDialog::navigationRequested(const QString& url) { + qDebug() << url; + if (Application::getInstance()->canAcceptURL(url)) { + if (Application::getInstance()->acceptURL(url)) { + return false; // we handled it, so QWebPage doesn't need to handle it + } + } + return true; +} diff --git a/interface/src/ui/MarketplaceDialog.h b/interface/src/ui/MarketplaceDialog.h new file mode 100644 index 0000000000..2440c3e07c --- /dev/null +++ b/interface/src/ui/MarketplaceDialog.h @@ -0,0 +1,29 @@ +// +// MarketplaceDialog.h +// +// Created by Bradley Austin Davis on 2015/04/14 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_MarketplaceDialog_h +#define hifi_MarketplaceDialog_h + +#include + +class MarketplaceDialog : public OffscreenQmlDialog +{ + Q_OBJECT + HIFI_QML_DECL + +public: + MarketplaceDialog(QQuickItem* parent = nullptr); + + Q_INVOKABLE bool navigationRequested(const QString& url); + +}; + +#endif diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index f892deabb6..e1fb0ce481 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -44,8 +44,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); - QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); - ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); + // FIXME: menu isn't prepared at this point. + //QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); + //ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); _scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.sort(0, Qt::AscendingOrder); diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index bb145a209f..9d10184d13 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -108,8 +108,6 @@ public: bool isMuted() { return _muted; } - void setIsStereoInput(bool isStereoInput); - const AudioIOStats& getStats() const { return _stats; } float getInputRingBufferMsecsAvailable() const; @@ -146,14 +144,14 @@ public slots: virtual void enableAudioSourceInject(bool enable); virtual void selectAudioSourcePinkNoise(); virtual void selectAudioSourceSine440(); - + + virtual void setIsStereoInput(bool stereo); + void toggleAudioNoiseReduction() { _isNoiseGateEnabled = !_isNoiseGateEnabled; } void toggleLocalEcho() { _shouldEchoLocally = !_shouldEchoLocally; } void toggleServerEcho() { _shouldEchoToServer = !_shouldEchoToServer; } - void toggleStereoInput() { setIsStereoInput(!_isStereoInput); } - void processReceivedSamples(const QByteArray& inputBuffer, QByteArray& outputBuffer); void sendMuteEnvironmentPacket(); diff --git a/libraries/audio/src/AbstractAudioInterface.h b/libraries/audio/src/AbstractAudioInterface.h index 9649d11ad2..a5855d75d1 100644 --- a/libraries/audio/src/AbstractAudioInterface.h +++ b/libraries/audio/src/AbstractAudioInterface.h @@ -31,6 +31,8 @@ public slots: virtual void enableAudioSourceInject(bool enable) = 0; virtual void selectAudioSourcePinkNoise() = 0; virtual void selectAudioSourceSine440() = 0; + + virtual void setIsStereoInput(bool stereo) = 0; }; Q_DECLARE_METATYPE(AbstractAudioInterface*) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index ae3a8c3e5c..d389846796 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -55,6 +55,17 @@ bool AvatarHashMap::containsAvatarWithDisplayName(const QString& displayName) { return !avatarWithDisplayName(displayName).isNull(); } +bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range) { + foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { + glm::vec3 avatarPosition = sharedAvatar->getPosition(); + float distance = glm::distance(avatarPosition, position); + if (distance < range) { + return true; + } + } + return false; +} + AvatarWeakPointer AvatarHashMap::avatarWithDisplayName(const QString& displayName) { foreach(const AvatarSharedPointer& sharedAvatar, _avatarHash) { if (sharedAvatar->getDisplayName() == displayName) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index b59559e78c..b7d40e2acc 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -20,6 +20,7 @@ #include #include "AvatarData.h" +#include typedef QSharedPointer AvatarSharedPointer; typedef QWeakPointer AvatarWeakPointer; @@ -36,6 +37,7 @@ public: public slots: void processAvatarMixerDatagram(const QByteArray& datagram, const QWeakPointer& mixerWeakPointer); bool containsAvatarWithDisplayName(const QString& displayName); + bool isAvatarInRange(const glm::vec3 & position, const float range); AvatarWeakPointer avatarWithDisplayName(const QString& displayname); private slots: diff --git a/libraries/octree/src/OctreePersistThread.cpp b/libraries/octree/src/OctreePersistThread.cpp index 9c7a43b12d..210d074001 100644 --- a/libraries/octree/src/OctreePersistThread.cpp +++ b/libraries/octree/src/OctreePersistThread.cpp @@ -285,6 +285,7 @@ void OctreePersistThread::restoreFromMostRecentBackup() { qCDebug(octree) << "DONE restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; } else { qCDebug(octree) << "ERROR while restoring backup file " << mostRecentBackupFileName << "to" << _filename << "..."; + perror("ERROR while restoring backup file"); } } else { qCDebug(octree) << "NO BEST backup file found."; @@ -366,6 +367,7 @@ void OctreePersistThread::rollOldBackupVersions(const BackupRule& rule) { qCDebug(octree) << "DONE rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; } else { qCDebug(octree) << "ERROR in rolling backup file " << backupFilenameN << "to" << backupFilenameNplusOne << "..."; + perror("ERROR in rolling backup file"); } } } @@ -425,6 +427,7 @@ void OctreePersistThread::backup() { rule.lastBackup = now; // only record successful backup in this case. } else { qCDebug(octree) << "ERROR in backing up persist file..."; + perror("ERROR in backing up persist file"); } } else { qCDebug(octree) << "persist file " << _filename << " does not exist. " << diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index 6d1e295703..eebd8f0c88 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -41,8 +41,8 @@ protected: DynamicCharacterController::DynamicCharacterController(AvatarData* avatarData) { _halfHeight = 1.0f; - _shape = NULL; - _rigidBody = NULL; + _shape = nullptr; + _rigidBody = nullptr; assert(avatarData); _avatarData = avatarData; @@ -262,7 +262,7 @@ void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld->removeRigidBody(_rigidBody); _dynamicsWorld->removeAction(this); } - _dynamicsWorld = NULL; + _dynamicsWorld = nullptr; } if (world && _rigidBody) { _dynamicsWorld = world; @@ -292,9 +292,9 @@ void DynamicCharacterController::updateShapeIfNecessary() { _pendingFlags &= ~ PENDING_FLAG_UPDATE_SHAPE; // delete shape and RigidBody delete _rigidBody; - _rigidBody = NULL; + _rigidBody = nullptr; delete _shape; - _shape = NULL; + _shape = nullptr; // compute new dimensions from avatar's bounding box float x = _boxScale.x; @@ -314,7 +314,7 @@ void DynamicCharacterController::updateShapeIfNecessary() { // create new body float mass = 1.0f; btVector3 inertia(1.0f, 1.0f, 1.0f); - _rigidBody = new btRigidBody(mass, NULL, _shape, inertia); + _rigidBody = new btRigidBody(mass, nullptr, _shape, inertia); _rigidBody->setSleepingThresholds(0.0f, 0.0f); _rigidBody->setAngularFactor(0.0f); _rigidBody->setWorldTransform(btTransform(glmToBullet(_avatarData->getOrientation()), diff --git a/libraries/physics/src/DynamicCharacterController.h b/libraries/physics/src/DynamicCharacterController.h index f98343c49b..06ff555f66 100644 --- a/libraries/physics/src/DynamicCharacterController.h +++ b/libraries/physics/src/DynamicCharacterController.h @@ -30,7 +30,7 @@ protected: glm::vec3 _shapeLocalOffset; glm::vec3 _boxScale; // used to compute capsule shape - AvatarData* _avatarData = NULL; + AvatarData* _avatarData = nullptr; bool _enabled; bool _isOnGround; @@ -41,7 +41,7 @@ protected: quint64 _jumpToHoverStart; uint32_t _pendingFlags; - btDynamicsWorld* _dynamicsWorld = NULL; + btDynamicsWorld* _dynamicsWorld = nullptr; btScalar _jumpSpeed; @@ -78,7 +78,8 @@ public: bool needsRemoval() const; bool needsAddition() const; void setEnabled(bool enabled); - bool isEnabled() const { return _enabled; } + bool isEnabled() const { return _enabled && _dynamicsWorld; } + void setDynamicsWorld(btDynamicsWorld* world); void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); diff --git a/libraries/script-engine/src/AudioScriptingInterface.cpp b/libraries/script-engine/src/AudioScriptingInterface.cpp index 8bcc4a20cf..e210ee6f6e 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.cpp +++ b/libraries/script-engine/src/AudioScriptingInterface.cpp @@ -89,4 +89,8 @@ void AudioScriptingInterface::selectSine440() { } } - +void AudioScriptingInterface::setStereoInput(bool stereo) { + if (_localAudioInterface) { + _localAudioInterface->setIsStereoInput(stereo); + } +} diff --git a/libraries/script-engine/src/AudioScriptingInterface.h b/libraries/script-engine/src/AudioScriptingInterface.h index 9d02b8c9a1..bbc9a57db8 100644 --- a/libraries/script-engine/src/AudioScriptingInterface.h +++ b/libraries/script-engine/src/AudioScriptingInterface.h @@ -32,6 +32,8 @@ protected: Q_INVOKABLE void injectGeneratedNoise(bool inject); Q_INVOKABLE void selectPinkNoise(); Q_INVOKABLE void selectSine440(); + + Q_INVOKABLE void setStereoInput(bool stereo); signals: void mutedByMixer(); diff --git a/libraries/script-engine/src/ScriptAudioInjector.cpp b/libraries/script-engine/src/ScriptAudioInjector.cpp index a9cf40558c..2ec30ad4dd 100644 --- a/libraries/script-engine/src/ScriptAudioInjector.cpp +++ b/libraries/script-engine/src/ScriptAudioInjector.cpp @@ -13,8 +13,12 @@ #include "ScriptAudioInjector.h" QScriptValue injectorToScriptValue(QScriptEngine* engine, ScriptAudioInjector* const& in) { + // The AudioScriptingInterface::playSound method can return null, so we need to account for that. + if (!in) { + return QScriptValue(QScriptValue::NullValue); + } + // when the script goes down we want to cleanup the injector - QObject::connect(engine, &QScriptEngine::destroyed, in, &ScriptAudioInjector::stopInjectorImmediately, Qt::DirectConnection); diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp deleted file mode 100644 index 3edcaebead..0000000000 --- a/libraries/ui/src/HifiMenu.cpp +++ /dev/null @@ -1,289 +0,0 @@ -// -// HifiMenu.cpp -// -// Created by Bradley Austin Davis on 2015/04/21 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#include "HifiMenu.h" -#include - -// FIXME can this be made a class member? -static const QString MENU_SUFFIX{ "__Menu" }; - -HIFI_QML_DEF_LAMBDA(HifiMenu, [=](QQmlContext* context, QObject* newItem) { - auto offscreenUi = DependencyManager::get(); - QObject * rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); - Q_ASSERT(rootMenu); - static_cast(newItem)->setRootMenu(rootMenu); - context->setContextProperty("rootMenu", rootMenu); -}); - -HifiMenu::HifiMenu(QQuickItem* parent) : QQuickItem(parent), _triggerMapper(this), _toggleMapper(this) { - this->setEnabled(false); - connect(&_triggerMapper, SIGNAL(mapped(QString)), this, SLOT(onTriggeredByName(const QString &))); - connect(&_toggleMapper, SIGNAL(mapped(QString)), this, SLOT(onToggledByName(const QString &))); -} - -void HifiMenu::onTriggeredByName(const QString & name) { - qDebug() << name << " triggered"; - if (_triggerActions.count(name)) { - _triggerActions[name](); - } -} - -void HifiMenu::onToggledByName(const QString & name) { - qDebug() << name << " toggled"; - if (_toggleActions.count(name)) { - QObject* menu = findMenuObject(name); - bool checked = menu->property("checked").toBool(); - _toggleActions[name](checked); - } -} - -void HifiMenu::setToggleAction(const QString & name, std::function f) { - _toggleActions[name] = f; -} - -void HifiMenu::setTriggerAction(const QString & name, std::function f) { - _triggerActions[name] = f; -} - -QObject* addMenu(QObject* parent, const QString & text) { - // FIXME add more checking here to ensure no name conflicts - QVariant returnedValue; - QMetaObject::invokeMethod(parent, "addMenu", Qt::DirectConnection, - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, text)); - QObject* result = returnedValue.value(); - if (result) { - result->setObjectName(text + MENU_SUFFIX); - } - return result; -} - -class QQuickMenuItem; -QObject* addItem(QObject* parent, const QString& text) { - // FIXME add more checking here to ensure no name conflicts - QQuickMenuItem* returnedValue{ nullptr }; - bool invokeResult = QMetaObject::invokeMethod(parent, "addItem", Qt::DirectConnection, - Q_RETURN_ARG(QQuickMenuItem*, returnedValue), - Q_ARG(QString, text)); - Q_ASSERT(invokeResult); - QObject* result = reinterpret_cast(returnedValue); - return result; -} - -const QObject* HifiMenu::findMenuObject(const QString & menuOption) const { - if (menuOption.isEmpty()) { - return _rootMenu; - } - const QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); - return result; -} - -QObject* HifiMenu::findMenuObject(const QString & menuOption) { - if (menuOption.isEmpty()) { - return _rootMenu; - } - QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); - return result; -} - -void HifiMenu::addMenu(const QString & parentMenu, const QString & menuOption) { - QObject* parent = findMenuObject(parentMenu); - QObject* result = ::addMenu(parent, menuOption); - Q_ASSERT(result); - result->setObjectName(menuOption + MENU_SUFFIX); - Q_ASSERT(findMenuObject(menuOption)); -} - -void HifiMenu::removeMenu(const QString& menuName) { - QObject* menu = findMenuObject(menuName); - Q_ASSERT(menu); - Q_ASSERT(menu != _rootMenu); - QMetaObject::invokeMethod(menu->parent(), "removeItem", - Q_ARG(QVariant, QVariant::fromValue(menu))); -} - -bool HifiMenu::menuExists(const QString& menuName) const { - return findMenuObject(menuName); -} - -void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorName) { - QObject * parent = findMenuObject(parentMenu); - bool invokeResult = QMetaObject::invokeMethod(parent, "addSeparator", Qt::DirectConnection); - Q_ASSERT(invokeResult); - addItem(parentMenu, separatorName); - enableItem(separatorName, false); -} - -void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) { -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption) { - QObject* parent = findMenuObject(parentMenu); - Q_ASSERT(parent); - QObject* result = ::addItem(parent, menuOption); - Q_ASSERT(result); - result->setObjectName(menuOption + MENU_SUFFIX); - Q_ASSERT(findMenuObject(menuOption)); - - _triggerMapper.setMapping(result, menuOption); - connect(result, SIGNAL(triggered()), &_triggerMapper, SLOT(map())); - - _toggleMapper.setMapping(result, menuOption); - connect(result, SIGNAL(toggled(bool)), &_toggleMapper, SLOT(map())); -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, std::function f) { - setTriggerAction(menuOption, f); - addItem(parentMenu, menuOption); -} - -void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, QObject* receiver, const char* slot) { - addItem(parentMenu, menuOption); - connectItem(menuOption, receiver, slot); -} - -void HifiMenu::removeItem(const QString& menuOption) { - removeMenu(menuOption); -} - -bool HifiMenu::itemExists(const QString& menuName, const QString& menuitem) const { - return findMenuObject(menuName); -} - -void HifiMenu::triggerItem(const QString& menuOption) { - QObject* menuItem = findMenuObject(menuOption); - Q_ASSERT(menuItem); - Q_ASSERT(menuItem != _rootMenu); - QMetaObject::invokeMethod(menuItem, "trigger"); -} - -QHash warned; -void warn(const QString & menuOption) { - if (!warned.contains(menuOption)) { - warned[menuOption] = menuOption; - qWarning() << "No menu item: " << menuOption; - } -} - -bool HifiMenu::isChecked(const QString& menuOption) const { - const QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return false; - } - return menuItem->property("checked").toBool(); -} - -void HifiMenu::setChecked(const QString& menuOption, bool isChecked) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - if (menuItem->property("checked").toBool() != isChecked) { - menuItem->setProperty("checked", QVariant::fromValue(isChecked)); - Q_ASSERT(menuItem->property("checked").toBool() == isChecked); - } -} - -void HifiMenu::setCheckable(const QString& menuOption, bool checkable) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - - menuItem->setProperty("checkable", QVariant::fromValue(checkable)); - Q_ASSERT(menuItem->property("checkable").toBool() == checkable); -} - -void HifiMenu::setItemText(const QString& menuOption, const QString& text) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - if (menuItem->property("type").toInt() == 2) { - menuItem->setProperty("title", QVariant::fromValue(text)); - } else { - menuItem->setProperty("text", QVariant::fromValue(text)); - } -} - -void HifiMenu::setRootMenu(QObject* rootMenu) { - _rootMenu = rootMenu; -} - -void HifiMenu::enableItem(const QString & menuOption, bool enabled) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; - } - menuItem->setProperty("enabled", QVariant::fromValue(enabled)); -} - -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) { - addItem(parentMenu, menuOption); - setCheckable(menuOption); - if (checked) { - setChecked(menuOption, checked); - } -} - -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { - setToggleAction(menuOption, f); - addCheckableItem(parentMenu, menuOption, checked); -} - -void HifiMenu::setItemVisible(const QString& menuOption, bool visible) { - QObject* result = findMenuObject(menuOption); - if (result) { - result->setProperty("visible", visible); - } -} - -bool HifiMenu::isItemVisible(const QString& menuOption) { - QObject* result = findMenuObject(menuOption); - if (result) { - return result->property("visible").toBool(); - } - return false; -} - -void HifiMenu::setItemShortcut(const QString& menuOption, const QString& shortcut) { - QObject* result = findMenuObject(menuOption); - if (result) { - result->setProperty("shortcut", shortcut); - } -} - -QString HifiMenu::getItemShortcut(const QString& menuOption) { - QObject* result = findMenuObject(menuOption); - if (result) { - return result->property("shortcut").toString(); - } - return QString(); -} - -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot) { - addCheckableItem(parentMenu, menuOption, checked); - connectItem(menuOption, receiver, slot); -} - -void HifiMenu::connectCheckable(const QString& menuOption, QObject* receiver, const char* slot) { - QObject* result = findMenuObject(menuOption); - connect(result, SIGNAL(toggled(bool)), receiver, slot); -} - -void HifiMenu::connectItem(const QString& menuOption, QObject* receiver, const char* slot) { - QObject* result = findMenuObject(menuOption); - connect(result, SIGNAL(triggered()), receiver, slot); -} diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h deleted file mode 100644 index 0adc485211..0000000000 --- a/libraries/ui/src/HifiMenu.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// HifiMenu.h -// -// Created by Bradley Austin Davis on 2015/04/21 -// Copyright 2015 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -// - -#pragma once -#ifndef hifi_HifiMenu_h -#define hifi_HifiMenu_h - -#include -#include -#include -#include -#include "OffscreenUi.h" - -class HifiMenu : public QQuickItem { - Q_OBJECT - HIFI_QML_DECL_LAMBDA - -public: - HifiMenu(QQuickItem* parent = nullptr); - - void setToggleAction(const QString& name, std::function f); - void setTriggerAction(const QString& name, std::function f); - - void addMenu(const QString& parentMenu, const QString& menuOption); - void removeMenu(const QString& menuName); - bool menuExists(const QString& menuName) const; - - void addSeparator(const QString& menuName, const QString& separatorName); - void removeSeparator(const QString& menuName, const QString& separatorName); - - void addItem(const QString& parentMenu, const QString& menuOption); - void addItem(const QString& parentMenu, const QString& menuOption, std::function f); - void addItem(const QString& parentMenu, const QString& menuOption, QObject* receiver, const char* slot); - - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked = false); - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f); - void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot); - void connectCheckable(const QString& menuOption, QObject* receiver, const char* slot); - void connectItem(const QString& menuOption, QObject* receiver, const char* slot); - - void removeItem(const QString& menuitem); - bool itemExists(const QString& menuName, const QString& menuitem) const; - void triggerItem(const QString& menuOption); - void enableItem(const QString& menuOption, bool enabled = true); - bool isChecked(const QString& menuOption) const; - void setChecked(const QString& menuOption, bool checked = true); - void setCheckable(const QString& menuOption, bool checkable = true); - void setExclusiveGroup(const QString& menuOption, const QString& groupName); - void setItemText(const QString& menuOption, const QString& text); - void setItemVisible(const QString& menuOption, bool visible = true); - bool isItemVisible(const QString& menuOption); - - void setItemShortcut(const QString& menuOption, const QString& shortcut); - QString getItemShortcut(const QString& menuOption); - - void setRootMenu(QObject* rootMenu); - -private slots: - void onTriggeredByName(const QString& name); - void onToggledByName(const QString& name); - -protected: - QHash> _triggerActions; - QHash> _toggleActions; - QObject* findMenuObject(const QString& name); - const QObject* findMenuObject(const QString& name) const; - QObject* _rootMenu{ nullptr }; - QSignalMapper _triggerMapper; - QSignalMapper _toggleMapper; -}; - -#endif // hifi_HifiMenu_h diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 95e847d24b..d9c7cd890d 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -124,9 +124,9 @@ void OffscreenUi::resize(const QSize& newSize) { if (_quickWindow) { _quickWindow->setGeometry(QRect(QPoint(), newSize)); + _quickWindow->contentItem()->setSize(newSize); } - _quickWindow->contentItem()->setSize(newSize); // Update our members if (_rootItem) { @@ -145,13 +145,12 @@ void OffscreenUi::setBaseUrl(const QUrl& baseUrl) { } QObject* OffscreenUi::load(const QUrl& qmlSource, std::function f) { - qDebug() << "Loading QML from URL " << qmlSource; _qmlComponent->loadUrl(qmlSource); if (_qmlComponent->isLoading()) { connect(_qmlComponent, &QQmlComponent::statusChanged, this, - [this, f](QQmlComponent::Status){ - finishQmlLoad(f); - }); + [this, f](QQmlComponent::Status){ + finishQmlLoad(f); + }); return nullptr; } @@ -360,10 +359,14 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) { QMouseEvent* mouseEvent = static_cast(event); QPointF originalPos = mouseEvent->localPos(); QPointF transformedPos = _mouseTranslator(originalPos); + transformedPos = mapWindowToUi(transformedPos, originalDestination); QMouseEvent mappedEvent(mouseEvent->type(), - mapWindowToUi(transformedPos, originalDestination), + transformedPos, mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), mouseEvent->modifiers()); + if (event->type() == QEvent::MouseMove) { + _qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos); + } mappedEvent.ignore(); if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { return mappedEvent.isAccepted(); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 84626cab52..ce40bec943 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -169,11 +169,11 @@ public: ButtonCallback callback = NO_OP_CALLBACK, QMessageBox::StandardButtons buttons = QMessageBox::Ok); -protected: +private: + QObject* finishQmlLoad(std::function f); private slots: void updateQuick(); - QObject* finishQmlLoad(std::function f); public slots: void requestUpdate(); diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp new file mode 100644 index 0000000000..5c0f8fb732 --- /dev/null +++ b/libraries/ui/src/VrMenu.cpp @@ -0,0 +1,218 @@ +// +// VrMenu.cpp +// +// Created by Bradley Austin Davis on 2015/04/21 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "VrMenu.h" +#include +#include + +// Binds together a Qt Action or Menu with the QML Menu or MenuItem +// +// TODO On reflection, it may be pointless to use the UUID. Perhaps +// simply creating the bidirectional link pointing to both the widget +// and qml object and inject the pointer into both objects +class MenuUserData : public QObjectUserData { + static const int USER_DATA_ID; + +public: + MenuUserData(QAction* action, QObject* qmlObject) { + init(action, qmlObject); + } + MenuUserData(QMenu* menu, QObject* qmlObject) { + init(menu, qmlObject); + } + + const QUuid uuid{ QUuid::createUuid() }; + + static MenuUserData* forObject(QObject* object) { + return static_cast(object->userData(USER_DATA_ID)); + } + +private: + MenuUserData(const MenuUserData&); + + void init(QObject* widgetObject, QObject* qmlObject) { + widgetObject->setUserData(USER_DATA_ID, this); + qmlObject->setUserData(USER_DATA_ID, this); + qmlObject->setObjectName(uuid.toString()); + // Make sure we can find it again in the future + Q_ASSERT(VrMenu::_instance->findMenuObject(uuid.toString())); + } +}; + +const int MenuUserData::USER_DATA_ID = QObject::registerUserData(); + +HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) { + auto offscreenUi = DependencyManager::get(); + QObject* rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + Q_ASSERT(rootMenu); + static_cast(newItem)->setRootMenu(rootMenu); + context->setContextProperty("rootMenu", rootMenu); +}); + +VrMenu* VrMenu::_instance{ nullptr }; +static QQueue> queuedLambdas; + +void VrMenu::executeOrQueue(std::function f) { + if (_instance) { + foreach(std::function priorLambda, queuedLambdas) { + priorLambda(_instance); + } + f(_instance); + } else { + queuedLambdas.push_back(f); + } +} + +void VrMenu::executeQueuedLambdas() { + Q_ASSERT(_instance); + foreach(std::function f, queuedLambdas) { + f(_instance); + } + queuedLambdas.clear(); +} + +VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { + _instance = this; + this->setEnabled(false); +} + + +// QML helper functions +QObject* addMenu(QObject* parent, const QString& text) { + // FIXME add more checking here to ensure no name conflicts + QVariant returnedValue; + QMetaObject::invokeMethod(parent, "addMenu", Qt::DirectConnection, + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, text)); + QObject* result = returnedValue.value(); + if (result) { + result->setObjectName(text); + } + return result; +} + +class QQuickMenuItem; +QObject* addItem(QObject* parent, const QString& text) { + // FIXME add more checking here to ensure no name conflicts + QQuickMenuItem* returnedValue{ nullptr }; + bool invokeResult = QMetaObject::invokeMethod(parent, "addItem", Qt::DirectConnection, + Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_ARG(QString, text)); + Q_ASSERT(invokeResult); + QObject* result = reinterpret_cast(returnedValue); + return result; +} + +const QObject* VrMenu::findMenuObject(const QString& menuOption) const { + if (menuOption.isEmpty()) { + return _rootMenu; + } + const QObject* result = _rootMenu->findChild(menuOption); + return result; +} + +QObject* VrMenu::findMenuObject(const QString& menuOption) { + if (menuOption.isEmpty()) { + return _rootMenu; + } + QObject* result = _rootMenu->findChild(menuOption); + return result; +} + +void VrMenu::setRootMenu(QObject* rootMenu) { + _rootMenu = rootMenu; +} + +void VrMenu::addMenu(QMenu* menu) { + Q_ASSERT(!MenuUserData::forObject(menu)); + QObject * parent = menu->parent(); + QObject * qmlParent; + if (dynamic_cast(parent)) { + MenuUserData* userData = MenuUserData::forObject(parent); + qmlParent = findMenuObject(userData->uuid.toString()); + } else if (dynamic_cast(parent)) { + qmlParent = _rootMenu; + } else { + Q_ASSERT(false); + } + QObject* result = ::addMenu(qmlParent, menu->title()); + new MenuUserData(menu, result); +} + +void updateQmlItemFromAction(QObject* target, QAction* source) { + target->setProperty("checkable", source->isCheckable()); + target->setProperty("enabled", source->isEnabled()); + target->setProperty("visible", source->isVisible()); + target->setProperty("text", source->text()); + target->setProperty("checked", source->isChecked()); +} + +void bindActionToQmlAction(QObject* qmlAction, QAction* action) { + new MenuUserData(action, qmlAction); + updateQmlItemFromAction(qmlAction, action); + QObject::connect(action, &QAction::changed, [=] { + updateQmlItemFromAction(qmlAction, action); + }); + QObject::connect(action, &QAction::toggled, [=](bool checked) { + qmlAction->setProperty("checked", checked); + }); + QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger())); +} + +void VrMenu::addAction(QMenu* menu, QAction* action) { + Q_ASSERT(!MenuUserData::forObject(action)); + Q_ASSERT(MenuUserData::forObject(menu)); + MenuUserData* userData = MenuUserData::forObject(menu); + QObject* parent = findMenuObject(userData->uuid.toString()); + Q_ASSERT(parent); + QObject* result = ::addItem(parent, action->text()); + Q_ASSERT(result); + // Bind the QML and Widget together + bindActionToQmlAction(result, action); +} + +void VrMenu::insertAction(QAction* before, QAction* action) { + QObject* beforeQml{ nullptr }; + { + MenuUserData* beforeUserData = MenuUserData::forObject(before); + Q_ASSERT(beforeUserData); + beforeQml = findMenuObject(beforeUserData->uuid.toString()); + } + + QObject* menu = beforeQml->parent(); + int index{ -1 }; + QVariant itemsVar = menu->property("items"); + QList items = itemsVar.toList(); + // FIXME add more checking here to ensure no name conflicts + for (index = 0; index < items.length(); ++index) { + QObject* currentQmlItem = items.at(index).value(); + if (currentQmlItem == beforeQml) { + break; + } + } + + QObject* result{ nullptr }; + if (index < 0 || index >= items.length()) { + result = ::addItem(menu, action->text()); + } else { + QQuickMenuItem* returnedValue{ nullptr }; + bool invokeResult = QMetaObject::invokeMethod(menu, "insertItem", Qt::DirectConnection, + Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_ARG(int, index), Q_ARG(QString, action->text())); + Q_ASSERT(invokeResult); + result = reinterpret_cast(returnedValue); + } + Q_ASSERT(result); + bindActionToQmlAction(result, action); +} + +void VrMenu::removeAction(QAction* action) { + // FIXME implement +} diff --git a/libraries/ui/src/VrMenu.h b/libraries/ui/src/VrMenu.h new file mode 100644 index 0000000000..06a16588af --- /dev/null +++ b/libraries/ui/src/VrMenu.h @@ -0,0 +1,49 @@ +// +// VrMenu.h +// +// Created by Bradley Austin Davis on 2015/04/21 +// Copyright 2015 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#pragma once +#ifndef hifi_VrMenu_h +#define hifi_VrMenu_h + +#include +#include +#include +#include +#include +#include +#include "OffscreenUi.h" + +// FIXME break up the rendering code (VrMenu) and the code for mirroring a Widget based menu in QML +class VrMenu : public QQuickItem { + Q_OBJECT + HIFI_QML_DECL_LAMBDA + +public: + static void executeOrQueue(std::function f); + static void executeQueuedLambdas(); + VrMenu(QQuickItem* parent = nullptr); + void addMenu(QMenu* menu); + void addAction(QMenu* parent, QAction* action); + void insertAction(QAction* before, QAction* action); + void removeAction(QAction* action); + + void setRootMenu(QObject* rootMenu); + +protected: + QObject* _rootMenu{ nullptr }; + + QObject* findMenuObject(const QString& name); + const QObject* findMenuObject(const QString& name) const; + + static VrMenu* _instance; + friend class MenuUserData; +}; + +#endif // hifi_VrMenu_h diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 5e45bf23a2..0ba7416b28 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -205,7 +205,7 @@ void QTestWindow::renderText() { { size.x + QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x + QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, { QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, - }; + }; QString str = QString::fromWCharArray(EXAMPLE_TEXT); for (int i = 0; i < 4; ++i) { diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 2ad2fa639c..a5bc50b288 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -35,7 +35,7 @@ #include #include "MessageDialog.h" -#include "HifiMenu.h" +#include "VrMenu.h" class RateCounter { std::vector times; @@ -74,7 +74,172 @@ public: }; -const QString & getQmlDir() { +class MenuConstants : public QObject{ + Q_OBJECT + Q_ENUMS(Item) + +public: + enum Item { + AboutApp, + AddRemoveFriends, + AddressBar, + AlignForearmsWithWrists, + AlternateIK, + AmbientOcclusion, + Animations, + Atmosphere, + Attachments, + AudioNoiseReduction, + AudioScope, + AudioScopeFiftyFrames, + AudioScopeFiveFrames, + AudioScopeFrames, + AudioScopePause, + AudioScopeTwentyFrames, + AudioStats, + AudioStatsShowInjectedStreams, + BandwidthDetails, + BlueSpeechSphere, + BookmarkLocation, + Bookmarks, + CascadedShadows, + CachesSize, + Chat, + Collisions, + Console, + ControlWithSpeech, + CopyAddress, + CopyPath, + DecreaseAvatarSize, + DeleteBookmark, + DisableActivityLogger, + DisableLightEntities, + DisableNackPackets, + DiskCacheEditor, + DisplayHands, + DisplayHandTargets, + DisplayModelBounds, + DisplayModelTriangles, + DisplayModelElementChildProxies, + DisplayModelElementProxy, + DisplayDebugTimingDetails, + DontDoPrecisionPicking, + DontFadeOnOctreeServerChanges, + DontRenderEntitiesAsScene, + EchoLocalAudio, + EchoServerAudio, + EditEntitiesHelp, + Enable3DTVMode, + EnableCharacterController, + EnableGlowEffect, + EnableVRMode, + ExpandMyAvatarSimulateTiming, + ExpandMyAvatarTiming, + ExpandOtherAvatarTiming, + ExpandPaintGLTiming, + ExpandUpdateTiming, + Faceshift, + FilterSixense, + FirstPerson, + FrameTimer, + Fullscreen, + FullscreenMirror, + GlowWhenSpeaking, + NamesAboveHeads, + GoToUser, + HMDTools, + IncreaseAvatarSize, + KeyboardMotorControl, + LeapMotionOnHMD, + LoadScript, + LoadScriptURL, + LoadRSSDKFile, + LodTools, + Login, + Log, + LowVelocityFilter, + Mirror, + MuteAudio, + MuteEnvironment, + NoFaceTracking, + NoShadows, + OctreeStats, + OffAxisProjection, + OnlyDisplayTopTen, + PackageModel, + Pair, + PipelineWarnings, + Preferences, + Quit, + ReloadAllScripts, + RenderBoundingCollisionShapes, + RenderFocusIndicator, + RenderHeadCollisionShapes, + RenderLookAtVectors, + RenderSkeletonCollisionShapes, + RenderTargetFramerate, + RenderTargetFramerateUnlimited, + RenderTargetFramerate60, + RenderTargetFramerate50, + RenderTargetFramerate40, + RenderTargetFramerate30, + RenderTargetFramerateVSyncOn, + RenderResolution, + RenderResolutionOne, + RenderResolutionTwoThird, + RenderResolutionHalf, + RenderResolutionThird, + RenderResolutionQuarter, + RenderAmbientLight, + RenderAmbientLightGlobal, + RenderAmbientLight0, + RenderAmbientLight1, + RenderAmbientLight2, + RenderAmbientLight3, + RenderAmbientLight4, + RenderAmbientLight5, + RenderAmbientLight6, + RenderAmbientLight7, + RenderAmbientLight8, + RenderAmbientLight9, + ResetAvatarSize, + ResetSensors, + RunningScripts, + RunTimingTests, + ScriptEditor, + ScriptedMotorControl, + ShowBordersEntityNodes, + ShowIKConstraints, + SimpleShadows, + SixenseEnabled, + SixenseMouseInput, + SixenseLasers, + ShiftHipsForIdleAnimations, + Stars, + Stats, + StereoAudio, + StopAllScripts, + SuppressShortTimings, + TestPing, + ToolWindow, + TransmitterDrive, + TurnWithHead, + UseAudioForMouth, + UseCamera, + VelocityFilter, + VisibleToEveryone, + VisibleToFriends, + VisibleToNoOne, + Wireframe, + }; + +public: + MenuConstants(QObject* parent = nullptr) : QObject(parent) { + + } +}; + +const QString& getQmlDir() { static QString dir; if (dir.isEmpty()) { QDir path(__FILE__); @@ -85,7 +250,7 @@ const QString & getQmlDir() { return dir; } -const QString & getTestQmlDir() { +const QString& getTestQmlDir() { static QString dir; if (dir.isEmpty()) { QDir path(__FILE__); @@ -100,7 +265,7 @@ const QString & getTestQmlDir() { class QTestWindow : public QWindow, private QOpenGLFunctions { Q_OBJECT - QOpenGLContext * _context{ nullptr }; + QOpenGLContext* _context{ nullptr }; QSize _size; bool _altPressed{ false }; RateCounter fps; @@ -108,7 +273,7 @@ class QTestWindow : public QWindow, private QOpenGLFunctions { int testQmlTexture{ 0 }; public: - QObject * rootMenu; + QObject* rootMenu; QTestWindow() { _timer.setInterval(1); @@ -139,7 +304,7 @@ public: initializeOpenGLFunctions(); { - QOpenGLDebugLogger *logger = new QOpenGLDebugLogger(this); + QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this); logger->initialize(); // initializes in the current context, i.e. ctx logger->enableMessages(); connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) { @@ -155,7 +320,9 @@ public: glDisable(GL_DEPTH_TEST); MessageDialog::registerType(); - HifiMenu::registerType(); + VrMenu::registerType(); + qmlRegisterType("Hifi", 1, 0, "MenuConstants"); + auto offscreenUi = DependencyManager::get(); offscreenUi->create(_context); @@ -181,24 +348,9 @@ public: #else offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->load(QUrl("RootMenu.qml")); - HifiMenu::load(); - QObject* menuObject = offscreenUi->getRootItem()->findChild("HifiMenu"); - HifiMenu* menu = offscreenUi->getRootItem()->findChild(); - menu->addMenu("", "File"); - menu->addItem("File", "Quit", []{ - QApplication::quit(); - }); - menu->addCheckableItem("File", "Toggle", false, [](bool toggled) { - qDebug() << "Toggle is " << toggled; - }); - menu->addMenu("", "Edit"); - menu->addItem("Edit", "Undo"); - menu->addItem("Edit", "Redo"); - menu->addItem("Edit", "Copy"); - menu->addItem("Edit", "Cut"); - menu->addItem("Edit", "Paste"); - menu->addMenu("", "Long Menu Name..."); + offscreenUi->load(QUrl("TestMenu.qml")); + // Requires a root menu to have been loaded before it can load + VrMenu::load(); #endif installEventFilter(offscreenUi.data()); offscreenUi->resume(); @@ -244,20 +396,16 @@ private: protected: - void resizeEvent(QResizeEvent * ev) override { + void resizeEvent(QResizeEvent* ev) override { resizeWindow(ev->size()); } - void keyPressEvent(QKeyEvent *event) { + void keyPressEvent(QKeyEvent* event) { _altPressed = Qt::Key_Alt == event->key(); switch (event->key()) { case Qt::Key_L: if (event->modifiers() & Qt::CTRL) { - auto offscreenUi = DependencyManager::get(); - HifiMenu * menu = offscreenUi->findChild(); - menu->addItem("", "Test 3"); - menu->addItem("File", "Test 3"); } break; case Qt::Key_K: @@ -280,11 +428,11 @@ protected: QQmlContext* menuContext{ nullptr }; void keyReleaseEvent(QKeyEvent *event) { if (_altPressed && Qt::Key_Alt == event->key()) { - HifiMenu::toggle(); + VrMenu::toggle(); } } - void moveEvent(QMoveEvent *event) { + void moveEvent(QMoveEvent* event) { static qreal oldPixelRatio = 0.0; if (devicePixelRatio() != oldPixelRatio) { oldPixelRatio = devicePixelRatio(); @@ -322,7 +470,7 @@ void QTestWindow::renderQml() { const char * LOG_FILTER_RULES = R"V0G0N( -*.debug=false +hifi.offscreen.focus.debug=false qt.quick.mouse.debug=false )V0G0N"; @@ -334,11 +482,9 @@ qt.quick.mouse.debug=false // return app.exec(); //} - - int main(int argc, char** argv) { QGuiApplication app(argc, argv); -// QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + QLoggingCategory::setFilterRules(LOG_FILTER_RULES); QTestWindow window; app.exec(); return 0;