From a9d65f168b2251f30a92b0ac9fe54be7ca4312ee Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 18 Apr 2015 23:50:56 -0700 Subject: [PATCH 01/33] Working on marketplace dialog --- interface/resources/qml/MarketplaceDialog.qml | 51 +++++++++++++++++++ interface/resources/qml/componentCreation.js | 29 ----------- interface/src/Application.cpp | 22 +++++++- .../scripting/WindowScriptingInterface.cpp | 2 +- interface/src/ui/MarketplaceDialog.cpp | 49 ++++++++++++++++++ interface/src/ui/MarketplaceDialog.h | 29 +++++++++++ tests/render-utils/src/main.cpp | 17 +++++++ 7 files changed, 168 insertions(+), 31 deletions(-) create mode 100644 interface/resources/qml/MarketplaceDialog.qml delete mode 100644 interface/resources/qml/componentCreation.js create mode 100644 interface/src/ui/MarketplaceDialog.cpp create mode 100644 interface/src/ui/MarketplaceDialog.h diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml new file mode 100644 index 0000000000..3fdd9b2b4d --- /dev/null +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -0,0 +1,51 @@ +import Hifi 1.0 +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Window 2.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Controls.Styles 1.3 +import QtWebKit 3.0 + +CustomDialog { + title: "Test Dlg" + id: testDialog + objectName: "Browser" + width: 720 + height: 720 + resizable: true + + MarketplaceDialog { + id: marketplaceDialog + } + + Item { + id: clientArea + // The client area + anchors.fill: parent + anchors.margins: parent.margins + anchors.topMargin: parent.topMargin + + + ScrollView { + anchors.fill: parent + WebView { + objectName: "WebView" + id: webview + url: "https://metaverse.highfidelity.com/marketplace" + anchors.fill: parent + onNavigationRequested: { + console.log(request.url) + if (!marketplaceDialog.navigationRequested(request.url)) { + console.log("Application absorbed the request") + request.action = WebView.IgnoreRequest; + return; + } + console.log("Application passed on the request") + request.action = WebView.AcceptRequest; + return; + } + } + } + + } +} diff --git a/interface/resources/qml/componentCreation.js b/interface/resources/qml/componentCreation.js deleted file mode 100644 index 15a828d6f8..0000000000 --- a/interface/resources/qml/componentCreation.js +++ /dev/null @@ -1,29 +0,0 @@ -var component; -var instance; -var parent; - -function createObject(parentObject, url) { - parent = parentObject; - component = Qt.createComponent(url); - if (component.status == Component.Ready) - finishCreation(); - else - component.statusChanged.connect(finishCreation); -} - -function finishCreation() { - if (component.status == Component.Ready) { - instance = component.createObject(parent, {"x": 100, "y": 100}); - if (instance == null) { - // Error Handling - console.log("Error creating object"); - } else { - instance.enabled = true - } - } else if (component.status == Component.Error) { - // Error Handling - console.log("Error loading component:", component.errorString()); - } else { - console.log("Unknown component status: " + component.status); - } -} \ No newline at end of file diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9ae54ac83e..8c93418153 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,6 +52,8 @@ #include #include #include +#include +#include #include #include @@ -82,6 +84,7 @@ #include #include #include +#include #include @@ -132,6 +135,7 @@ #include "ui/DialogsManager.h" #include "ui/InfoView.h" #include "ui/LoginDialog.h" +#include "ui/MarketplaceDialog.h" #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" @@ -743,12 +747,26 @@ void Application::initializeGL() { InfoView::showFirstTime(INFO_HELP_PATH); } +class OAuthFactory : public QQmlNetworkAccessManagerFactory { + QThreadStorage oauthNetworkAccessManagers; +public: + virtual QNetworkAccessManager * create(QObject * parent) { + if (!oauthNetworkAccessManagers.hasLocalData()) { + oauthNetworkAccessManagers.setLocalData(OAuthNetworkAccessManager::getInstance()); + } + return oauthNetworkAccessManagers.localData(); + } +}; + void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); + MarketplaceDialog::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); + offscreenUi->qmlEngine()->setNetworkAccessManagerFactory(new OAuthFactory()); + offscreenUi->qmlEngine()->networkAccessManager(); offscreenUi->resize(_glWidget->size()); offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); @@ -1129,7 +1147,9 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Backslash: - Menu::getInstance()->triggerOption(MenuOption::Chat); + MarketplaceDialog::show(); + + //Menu::getInstance()->triggerOption(MenuOption::Chat); break; case Qt::Key_Up: diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 6ed3e48274..46ca0c160f 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -51,7 +51,7 @@ void WindowScriptingInterface::setFocus() { } void WindowScriptingInterface::raiseMainWindow() { - Application::getInstance()->getWindow()->raise(); +// 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..34d3414ef4 --- /dev/null +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -0,0 +1,49 @@ +// +// AddressBarDialog.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 + +#include "DependencyManager.h" + +QML_DIALOG_DEF(MarketplaceDialog) + + +MarketplaceDialog::MarketplaceDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { + this-> +} + +bool MarketplaceDialog::navigationRequested(const QString & url) { + qDebug() << url; + if (Application::getInstance()->canAcceptURL(url)) { + qDebug() << "Trying to send url to the app"; + if (Application::getInstance()->acceptURL(url)) { + qDebug() << "Sent url to the app"; + return false; // we handled it, so QWebPage doesn't need to handle it + } + } + return true; +} + +//https://metaverse.highfidelity.com/marketplace + +// +//bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) { +// QString urlString = request.url().toString(); +// if (Application::getInstance()->canAcceptURL(urlString)) { +// if (Application::getInstance()->acceptURL(urlString)) { +// return false; // we handled it, so QWebPage doesn't need to handle it +// } +// } +// return true; +//} \ No newline at end of file diff --git a/interface/src/ui/MarketplaceDialog.h b/interface/src/ui/MarketplaceDialog.h new file mode 100644 index 0000000000..137bb0bea7 --- /dev/null +++ b/interface/src/ui/MarketplaceDialog.h @@ -0,0 +1,29 @@ +// +// AddressBarDialog.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 + QML_DIALOG_DECL + +public: + MarketplaceDialog(QQuickItem *parent = 0); + + Q_INVOKABLE bool navigationRequested(const QString & url); + +}; + +#endif diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 9d7363c241..28d542e51a 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -27,6 +27,8 @@ #include #include #include +#include + #include #include #include @@ -189,6 +191,8 @@ public: }); installEventFilter(offscreenUi.data()); offscreenUi->resume(); + QWebEnginePage *page = new QWebEnginePage; + page->runJavaScript("'Java''' 'Script'", [](const QVariant &result) { qDebug() << result; }); } virtual ~QTestWindow() { @@ -210,9 +214,22 @@ protected: switch (event->key()) { case Qt::Key_L: if (event->modifiers() & Qt::CTRL) { + auto offscreenUi = DependencyManager::get(); + offscreenUi->qmlEngine()->clearComponentCache(); DependencyManager::get()->toggle(QString("TestDialog.qml"), "TestDialog"); } break; + case Qt::Key_K: + if (event->modifiers() & Qt::CTRL) { + DependencyManager::get()->toggle(QString("Browser.qml"), "Browser"); + } + break; + case Qt::Key_J: + if (event->modifiers() & Qt::CTRL) { + QObject * obj = DependencyManager::get()->findObject("WebView"); + qDebug() << obj; + } + break; } QWindow::keyPressEvent(event); } From 8269b9925b372a09fb24bf1ec7b2e67c1a1e0310 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 20 Apr 2015 17:18:16 -0700 Subject: [PATCH 02/33] Adding first pass at message box --- interface/resources/qml/CustomButton.qml | 8 +- interface/resources/qml/MessageDialog.qml | 359 ++++++++++++++++++ interface/resources/qml/images/critical.png | Bin 0 -> 253 bytes .../resources/qml/images/information.png | Bin 0 -> 254 bytes interface/resources/qml/images/question.png | Bin 0 -> 257 bytes interface/resources/qml/images/warning.png | Bin 0 -> 224 bytes interface/src/Application.cpp | 4 +- interface/src/ui/LoginDialog.cpp | 4 +- interface/src/ui/MarketplaceDialog.cpp | 20 - libraries/render-utils/src/MessageDialog.cpp | 142 +++++++ libraries/render-utils/src/MessageDialog.h | 98 +++++ .../render-utils/src/OffscreenQmlDialog.cpp | 24 ++ .../render-utils/src/OffscreenQmlDialog.h | 60 ++- libraries/render-utils/src/OffscreenUi.cpp | 61 +-- libraries/render-utils/src/OffscreenUi.h | 31 +- 15 files changed, 740 insertions(+), 71 deletions(-) create mode 100644 interface/resources/qml/MessageDialog.qml create mode 100644 interface/resources/qml/images/critical.png create mode 100644 interface/resources/qml/images/information.png create mode 100644 interface/resources/qml/images/question.png create mode 100644 interface/resources/qml/images/warning.png create mode 100644 libraries/render-utils/src/MessageDialog.cpp create mode 100644 libraries/render-utils/src/MessageDialog.h diff --git a/interface/resources/qml/CustomButton.qml b/interface/resources/qml/CustomButton.qml index ce57d7ce5e..fb1b544207 100644 --- a/interface/resources/qml/CustomButton.qml +++ b/interface/resources/qml/CustomButton.qml @@ -6,9 +6,13 @@ import QtQuick.Controls.Styles 1.3 Button { SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } text: "Text" - width: 128 - height: 64 style: ButtonStyle { + padding { + top: 8 + left: 12 + right: 12 + bottom: 8 + } background: CustomBorder { anchors.fill: parent } diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml new file mode 100644 index 0000000000..f4a360baed --- /dev/null +++ b/interface/resources/qml/MessageDialog.qml @@ -0,0 +1,359 @@ +/***************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the QtQuick.Dialogs module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +*****************************************************************************/ + +import Hifi 1.0 as Hifi +import QtQuick 2.2 +import QtQuick.Controls 1.2 +import QtQuick.Window 2.1 +import QtQuick.Dialogs 1.2 + +CustomDialog { + id: root + property real spacing: 8 + property real outerSpacing: 16 + + destroyOnCloseButton: true + destroyOnInvisible: true + implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 + implicitWidth: Math.min(200, Math.max(mainText.implicitWidth, content.buttonsRowImplicitWidth) + outerSpacing * 2); + + 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 + } + + onEnabledChanged: { + if (enabled) { + content.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 + + Keys.onPressed: { + console.log("Key press at content") + event.accepted = true + if (event.modifiers === Qt.ControlModifier) + switch (event.key) { + case Qt.Key_A: + console.log("Select All") + detailedText.selectAll() + break + case Qt.Key_C: + console.log("Copy") + detailedText.copy() + break + case Qt.Key_Period: + if (Qt.platform.os === "osx") + reject() + break + } else switch (event.key) { + case Qt.Key_Escape: + case Qt.Key_Back: + console.log("Rejecting") + reject() + break + case Qt.Key_Enter: + case Qt.Key_Return: + console.log("Accepting") + accept() + break + } + } + + onImplicitWidthChanged: root.width = implicitWidth + + Component.onCompleted: { + root.title = title + } + + onTitleChanged: { + root.title = title + } + + Column { + id: contentColumn + spacing: root.outerSpacing + anchors { + top: parent.top + left: parent.left + right: parent.right + } + + Item { + width: parent.width + height: Math.max(icon.height, mainText.height + informativeText.height + root.spacing) + Image { + id: icon + source: content.standardIconSource + } + + Text { + id: mainText + anchors { + left: icon.right + leftMargin: root.spacing + right: parent.right + } + text: content.text + font.pointSize: 14 + font.weight: Font.Bold + wrapMode: Text.WordWrap + } + + Text { + id: informativeText + anchors { + left: icon.right + right: parent.right + top: mainText.bottom + leftMargin: root.spacing + topMargin: root.spacing + } + text: content.informativeText + font.pointSize: 14 + wrapMode: Text.WordWrap + } + } + + + Flow { + id: buttons + spacing: root.spacing + layoutDirection: Qt.RightToLeft + width: parent.width + CustomButton { + id: okButton + text: qsTr("OK") + onClicked: content.click(StandardButton.Ok) + visible: content.standardButtons & StandardButton.Ok + } + CustomButton { + id: openButton + text: qsTr("Open") + onClicked: content.click(StandardButton.Open) + visible: content.standardButtons & StandardButton.Open + } + CustomButton { + id: saveButton + text: qsTr("Save") + onClicked: content.click(StandardButton.Save) + visible: content.standardButtons & StandardButton.Save + } + CustomButton { + id: saveAllButton + text: qsTr("Save All") + onClicked: content.click(StandardButton.SaveAll) + visible: content.standardButtons & StandardButton.SaveAll + } + CustomButton { + id: retryButton + text: qsTr("Retry") + onClicked: content.click(StandardButton.Retry) + visible: content.standardButtons & StandardButton.Retry + } + CustomButton { + id: ignoreButton + text: qsTr("Ignore") + onClicked: content.click(StandardButton.Ignore) + visible: content.standardButtons & StandardButton.Ignore + } + CustomButton { + id: applyButton + text: qsTr("Apply") + onClicked: content.click(StandardButton.Apply) + visible: content.standardButtons & StandardButton.Apply + } + CustomButton { + id: yesButton + text: qsTr("Yes") + onClicked: content.click(StandardButton.Yes) + visible: content.standardButtons & StandardButton.Yes + } + CustomButton { + id: yesAllButton + text: qsTr("Yes to All") + onClicked: content.click(StandardButton.YesToAll) + visible: content.standardButtons & StandardButton.YesToAll + } + CustomButton { + id: noButton + text: qsTr("No") + onClicked: content.click(StandardButton.No) + visible: content.standardButtons & StandardButton.No + } + CustomButton { + id: noAllButton + text: qsTr("No to All") + onClicked: content.click(StandardButton.NoToAll) + visible: content.standardButtons & StandardButton.NoToAll + } + CustomButton { + id: discardButton + text: qsTr("Discard") + onClicked: content.click(StandardButton.Discard) + visible: content.standardButtons & StandardButton.Discard + } + CustomButton { + id: resetButton + text: qsTr("Reset") + onClicked: content.click(StandardButton.Reset) + visible: content.standardButtons & StandardButton.Reset + } + CustomButton { + id: restoreDefaultsButton + text: qsTr("Restore Defaults") + onClicked: content.click(StandardButton.RestoreDefaults) + visible: content.standardButtons & StandardButton.RestoreDefaults + } + CustomButton { + id: cancelButton + text: qsTr("Cancel") + onClicked: content.click(StandardButton.Cancel) + visible: content.standardButtons & StandardButton.Cancel + } + CustomButton { + id: abortButton + text: qsTr("Abort") + onClicked: content.click(StandardButton.Abort) + visible: content.standardButtons & StandardButton.Abort + } + CustomButton { + id: closeButton + text: qsTr("Close") + onClicked: content.click(StandardButton.Close) + visible: content.standardButtons & StandardButton.Close + } + CustomButton { + id: moreButton + text: qsTr("Show Details...") + onClicked: content.state = (content.state === "" ? "expanded" : "") + visible: content.detailedText.length > 0 + } + CustomButton { + id: helpButton + text: qsTr("Help") + onClicked: content.click(StandardButton.Help) + visible: content.standardButtons & StandardButton.Help + } + onVisibleChildrenChanged: root.calculateImplicitWidth() + } + } + + Item { + id: details + width: parent.width + implicitHeight: detailedText.implicitHeight + root.spacing + height: 0 + clip: true + + anchors { + left: parent.left + right: parent.right + top: contentColumn.bottom + topMargin: root.spacing + leftMargin: root.outerSpacing + rightMargin: root.outerSpacing + } + + Flickable { + id: flickable + contentHeight: detailedText.height + anchors.fill: parent + anchors.topMargin: root.spacing + anchors.bottomMargin: root.outerSpacing + TextEdit { + id: detailedText + text: content.detailedText + width: details.width + wrapMode: Text.WordWrap + readOnly: true + selectByMouse: true + } + } + } + + states: [ + State { + name: "expanded" + PropertyChanges { + target: details + height: root.height - contentColumn.height - root.spacing - root.outerSpacing + } + PropertyChanges { + target: content + implicitHeight: contentColumn.implicitHeight + root.spacing * 2 + + detailedText.implicitHeight + root.outerSpacing * 2 + } + PropertyChanges { + target: moreButton + text: qsTr("Hide Details") + } + } + ] + +/* + Rectangle { + + } + Component.onCompleted: calculateImplicitWidth() + */ + } +} diff --git a/interface/resources/qml/images/critical.png b/interface/resources/qml/images/critical.png new file mode 100644 index 0000000000000000000000000000000000000000..dc9c5aebf453dd92dba60c48f060b34f0087e02c GIT binary patch literal 253 zcmVkNDWE^AyB8@ZEC00000NkvXXu0mjf DKfYr$ literal 0 HcmV?d00001 diff --git a/interface/resources/qml/images/information.png b/interface/resources/qml/images/information.png new file mode 100644 index 0000000000000000000000000000000000000000..0a2eb87d108d2a24b71559998627570a252ebe69 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8`~f~8uBj!u3?T4-=FFM@ z|NrludHNay0|R48kY6x^!?PP{3=9l&JzX3_G|sP`c#-$80*{Mh+yV`sgwlqP>#QC( z>X#+u7`Xm@Q`=BsWqr<)MUc^=nfrazYu@_ss|q3QYrOu5+ux47bKp#oZChf4k#&aW z_6qNdiK4+zLkc4MIa@Ri#52@3K4zcMoZ;%Na4X>suaM`rgBP8<|7A}r*;L-N)XQ^~ z*Qd}2Q;+ng3Z7t^78=O>`qh(QX^u+vLUu-L1y=d3FMU5VRZj!?kipZ{&t;ucLK6Te CT3=!S literal 0 HcmV?d00001 diff --git a/interface/resources/qml/images/question.png b/interface/resources/qml/images/question.png new file mode 100644 index 0000000000000000000000000000000000000000..2dd92fd7915a09de670b8b6022ddcf02d4cc90e1 GIT binary patch literal 257 zcmV+c0sj7pP)NklQ}7)nBez2^$+jK{Nw%L3xK4m{g6jm}2|@>7z7zk+b}w*O&>f9X*ioevByCmJtfv0xgg* zX&;ac>>nrw_75mp9NLlK=){M}g!K&|3b4W-F*J23VSVK);JN=%IJYXN$un1x$~;9b a#PS7=(1SoC6O>K>0000a literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8c93418153..b7ba384a97 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -85,6 +85,7 @@ #include #include #include +#include #include @@ -762,11 +763,10 @@ void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); MarketplaceDialog::registerType(); + MessageDialog::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); - offscreenUi->qmlEngine()->setNetworkAccessManagerFactory(new OAuthFactory()); - offscreenUi->qmlEngine()->networkAccessManager(); offscreenUi->resize(_glWidget->size()); offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 4df42c0f15..5726818b2d 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -37,7 +37,9 @@ void LoginDialog::toggleAction() { } else { // change the menu item to login loginAction->setText("Login"); - connect(loginAction, &QAction::triggered, &LoginDialog::show); + connect(loginAction, &QAction::triggered, [] { + LoginDialog::show(); + }); } } diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp index 34d3414ef4..ed30a2c120 100644 --- a/interface/src/ui/MarketplaceDialog.cpp +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -10,40 +10,20 @@ #include "Application.h" #include "MarketplaceDialog.h" - - -#include - #include "DependencyManager.h" QML_DIALOG_DEF(MarketplaceDialog) MarketplaceDialog::MarketplaceDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { - this-> } bool MarketplaceDialog::navigationRequested(const QString & url) { qDebug() << url; if (Application::getInstance()->canAcceptURL(url)) { - qDebug() << "Trying to send url to the app"; if (Application::getInstance()->acceptURL(url)) { - qDebug() << "Sent url to the app"; return false; // we handled it, so QWebPage doesn't need to handle it } } return true; } - -//https://metaverse.highfidelity.com/marketplace - -// -//bool DataWebPage::acceptNavigationRequest(QWebFrame* frame, const QNetworkRequest& request, QWebPage::NavigationType type) { -// QString urlString = request.url().toString(); -// if (Application::getInstance()->canAcceptURL(urlString)) { -// if (Application::getInstance()->acceptURL(urlString)) { -// return false; // we handled it, so QWebPage doesn't need to handle it -// } -// } -// return true; -//} \ No newline at end of file diff --git a/libraries/render-utils/src/MessageDialog.cpp b/libraries/render-utils/src/MessageDialog.cpp new file mode 100644 index 0000000000..c16f5653e5 --- /dev/null +++ b/libraries/render-utils/src/MessageDialog.cpp @@ -0,0 +1,142 @@ +// +// +// MessageDialog.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 "MessageDialog.h" + +QML_DIALOG_DEF(MessageDialog) + + +MessageDialog::MessageDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { + _buttons = StandardButtons(Ok | Cancel); +} + +MessageDialog::~MessageDialog() { +} + +QString MessageDialog::text() const { + return _text; +} + +QString MessageDialog::informativeText() const { + return _informativeText; +} + +QString MessageDialog::detailedText() const { + return _detailedText; +} + +MessageDialog::Icon MessageDialog::icon() const { + return _icon; +} + +void MessageDialog::setVisible(bool v) { + OffscreenQmlDialog::setVisible(v); +} + +void MessageDialog::setText(const QString &arg) { + if (arg != _text) { + _text = arg; + emit textChanged(); + } +} + +void MessageDialog::setInformativeText(const QString &arg) { + if (arg != _informativeText) { + _informativeText = arg; + emit informativeTextChanged(); + } +} + +void MessageDialog::setDetailedText(const QString &arg) { + if (arg != _detailedText) { + _detailedText = arg; + emit detailedTextChanged(); + } +} + +void MessageDialog::setIcon(MessageDialog::Icon icon) { + if (icon != _icon) { + _icon = icon; + emit iconChanged(); + } +} + +void MessageDialog::setStandardButtons(StandardButtons buttons) { + if (buttons != _buttons) { + _buttons = buttons; + emit standardButtonsChanged(); + } +} + +void MessageDialog::click(StandardButton button) { + click(static_cast(button), + static_cast( + QPlatformDialogHelper::buttonRole(static_cast(button)))); +} + +QUrl MessageDialog::standardIconSource() { + switch (icon()) { + case QMessageDialogOptions::Information: + return QUrl("images/information.png"); + break; + case QMessageDialogOptions::Warning: + return QUrl("images/warning.png"); + break; + case QMessageDialogOptions::Critical: + return QUrl("images/critical.png"); + break; + case QMessageDialogOptions::Question: + return QUrl("images/question.png"); + break; + default: + return QUrl(); + break; + } +} + +MessageDialog::StandardButtons MessageDialog::standardButtons() const { + return _buttons; +} + +MessageDialog::StandardButton MessageDialog::clickedButton() const { + return _clickedButton; +} + +void MessageDialog::click(StandardButton button, QPlatformDialogHelper::ButtonRole) { + _clickedButton = button; + if (_resultCallback) { + _resultCallback(QMessageBox::StandardButton(_clickedButton)); + } + hide(); +} + +void MessageDialog::accept() { + // enter key is treated like OK + if (_clickedButton == NoButton) + _clickedButton = Ok; + if (_resultCallback) { + _resultCallback(QMessageBox::StandardButton(_clickedButton)); + } + OffscreenQmlDialog::accept(); +} + +void MessageDialog::reject() { + // escape key is treated like cancel + if (_clickedButton == NoButton) + _clickedButton = Cancel; + if (_resultCallback) { + _resultCallback(QMessageBox::StandardButton(_clickedButton)); + } + OffscreenQmlDialog::reject(); +} + +void MessageDialog::setResultCallback(OffscreenUi::ButtonCallback callback) { + _resultCallback = callback; +} diff --git a/libraries/render-utils/src/MessageDialog.h b/libraries/render-utils/src/MessageDialog.h new file mode 100644 index 0000000000..8b5a895068 --- /dev/null +++ b/libraries/render-utils/src/MessageDialog.h @@ -0,0 +1,98 @@ +// +// MessageDialog.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_MessageDialog_h +#define hifi_MessageDialog_h + +#include "OffscreenQmlDialog.h" +#include <5.4.1/QtGui/qpa/qplatformdialoghelper.h> + +class MessageDialog : public OffscreenQmlDialog +{ + Q_OBJECT + QML_DIALOG_DECL + +private: + Q_ENUMS(Icon) + Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) + Q_PROPERTY(QString informativeText READ informativeText WRITE setInformativeText NOTIFY informativeTextChanged) + Q_PROPERTY(QString detailedText READ detailedText WRITE setDetailedText NOTIFY detailedTextChanged) + Q_PROPERTY(Icon icon READ icon WRITE setIcon NOTIFY iconChanged) + Q_PROPERTY(QUrl standardIconSource READ standardIconSource NOTIFY iconChanged) + Q_PROPERTY(StandardButtons standardButtons READ standardButtons WRITE setStandardButtons NOTIFY standardButtonsChanged) + Q_PROPERTY(StandardButton clickedButton READ clickedButton NOTIFY buttonClicked) + +public: + enum Icon { + NoIcon = QMessageDialogOptions::NoIcon, + Information = QMessageDialogOptions::Information, + Warning = QMessageDialogOptions::Warning, + Critical = QMessageDialogOptions::Critical, + Question = QMessageDialogOptions::Question + }; + + + MessageDialog(QQuickItem *parent = 0); + virtual ~MessageDialog(); + + QString text() const; + QString informativeText() const; + QString detailedText() const; + Icon icon() const; + +public slots: + virtual void setVisible(bool v); + void setText(const QString &arg); + void setInformativeText(const QString &arg); + void setDetailedText(const QString &arg); + void setIcon(Icon icon); + void setStandardButtons(StandardButtons buttons); + void setResultCallback(OffscreenUi::ButtonCallback callback); + void click(StandardButton button); + QUrl standardIconSource(); + StandardButtons standardButtons() const; + StandardButton clickedButton() const; + +signals: + void textChanged(); + void informativeTextChanged(); + void detailedTextChanged(); + void iconChanged(); + void standardButtonsChanged(); + void buttonClicked(); + void discard(); + void help(); + void yes(); + void no(); + void apply(); + void reset(); + +protected slots: + virtual void click(StandardButton button, QPlatformDialogHelper::ButtonRole); + virtual void accept(); + virtual void reject(); + +private: + QString _title; + QString _text; + QString _informativeText; + QString _detailedText; + Icon _icon{ Information }; + StandardButtons _buttons; + StandardButton _clickedButton{ NoButton }; + OffscreenUi::ButtonCallback _resultCallback; +}; + +#endif // hifi_MessageDialog_h + + + + diff --git a/libraries/render-utils/src/OffscreenQmlDialog.cpp b/libraries/render-utils/src/OffscreenQmlDialog.cpp index d1e060245d..dbd621ad85 100644 --- a/libraries/render-utils/src/OffscreenQmlDialog.cpp +++ b/libraries/render-utils/src/OffscreenQmlDialog.cpp @@ -13,6 +13,30 @@ OffscreenQmlDialog::OffscreenQmlDialog(QQuickItem* parent) : QQuickItem(parent) { } +OffscreenQmlDialog::~OffscreenQmlDialog() { +} + void OffscreenQmlDialog::hide() { static_cast(parent())->setEnabled(false); } + +QString OffscreenQmlDialog::title() const { + return _title; +} + +void OffscreenQmlDialog::setTitle(const QString &arg) { + if (arg != _title) { + _title = arg; + emit titleChanged(); + } +} + +void OffscreenQmlDialog::accept() { + hide(); + emit accepted(); +} + +void OffscreenQmlDialog::reject() { + hide(); + emit rejected(); +} diff --git a/libraries/render-utils/src/OffscreenQmlDialog.h b/libraries/render-utils/src/OffscreenQmlDialog.h index eca82261c0..c6c95981a6 100644 --- a/libraries/render-utils/src/OffscreenQmlDialog.h +++ b/libraries/render-utils/src/OffscreenQmlDialog.h @@ -13,6 +13,8 @@ #define hifi_OffscreenQmlDialog_h #include +#include <5.4.1/QtGui/qpa/qplatformdialoghelper.h> + #include "OffscreenUi.h" #define QML_DIALOG_DECL \ @@ -21,8 +23,8 @@ private: \ static const QUrl QML; \ public: \ static void registerType(); \ - static void show(); \ - static void toggle(); \ + static void show(std::function f = [](QQmlContext*, QQuickItem*) {}); \ + static void toggle(std::function f = [](QQmlContext*, QQuickItem*) {}); \ private: #define QML_DIALOG_DEF(x) \ @@ -33,24 +35,70 @@ private: qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \ } \ \ - void x::show() { \ + void x::show(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ - offscreenUi->show(QML, NAME); \ + offscreenUi->show(QML, NAME, f); \ } \ \ - void x::toggle() { \ + void x::toggle(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ - offscreenUi->toggle(QML, NAME); \ + offscreenUi->toggle(QML, NAME, f); \ } class OffscreenQmlDialog : public QQuickItem { Q_OBJECT + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_ENUMS(StandardButton) + Q_FLAGS(StandardButtons) + public: OffscreenQmlDialog(QQuickItem* parent = nullptr); + virtual ~OffscreenQmlDialog(); + + enum StandardButton { + NoButton = QPlatformDialogHelper::NoButton, + Ok = QPlatformDialogHelper::Ok, + Save = QPlatformDialogHelper::Save, + SaveAll = QPlatformDialogHelper::SaveAll, + Open = QPlatformDialogHelper::Open, + Yes = QPlatformDialogHelper::Yes, + YesToAll = QPlatformDialogHelper::YesToAll, + No = QPlatformDialogHelper::No, + NoToAll = QPlatformDialogHelper::NoToAll, + Abort = QPlatformDialogHelper::Abort, + Retry = QPlatformDialogHelper::Retry, + Ignore = QPlatformDialogHelper::Ignore, + Close = QPlatformDialogHelper::Close, + Cancel = QPlatformDialogHelper::Cancel, + Discard = QPlatformDialogHelper::Discard, + Help = QPlatformDialogHelper::Help, + Apply = QPlatformDialogHelper::Apply, + Reset = QPlatformDialogHelper::Reset, + RestoreDefaults = QPlatformDialogHelper::RestoreDefaults, + NButtons + }; + Q_DECLARE_FLAGS(StandardButtons, StandardButton) protected: void hide(); + virtual void accept(); + virtual void reject(); + +public: + QString title() const; + void setTitle(const QString &arg); + +signals: + void accepted(); + void rejected(); + void titleChanged(); + +private: + QString _title; + }; +Q_DECLARE_OPERATORS_FOR_FLAGS(OffscreenQmlDialog::StandardButtons) + #endif diff --git a/libraries/render-utils/src/OffscreenUi.cpp b/libraries/render-utils/src/OffscreenUi.cpp index 837affab59..d25d78300a 100644 --- a/libraries/render-utils/src/OffscreenUi.cpp +++ b/libraries/render-utils/src/OffscreenUi.cpp @@ -13,6 +13,7 @@ #include #include #include +#include "MessageDialog.h" // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to @@ -140,13 +141,15 @@ void OffscreenUi::setBaseUrl(const QUrl& baseUrl) { _qmlEngine->setBaseUrl(baseUrl); } -void OffscreenUi::load(const QUrl& qmlSource, std::function f) { +void 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, []{}); - } else { - finishQmlLoad(); + if (_qmlComponent->isLoading()) + connect(_qmlComponent, &QQmlComponent::statusChanged, this, [this, f] { + finishQmlLoad(f); + }); + else { + finishQmlLoad(f); } } @@ -163,7 +166,7 @@ void OffscreenUi::requestRender() { } } -void OffscreenUi::finishQmlLoad() { +void OffscreenUi::finishQmlLoad(std::function f) { disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad); if (_qmlComponent->isError()) { QList errorList = _qmlComponent->errors(); @@ -197,7 +200,7 @@ void OffscreenUi::finishQmlLoad() { // Make sure we make items focusable (critical for // supporting keyboard shortcuts) newItem->setFlag(QQuickItem::ItemIsFocusScope, true); - + f(_qmlEngine->contextForObject(newItem), newItem); if (!_rootItem) { // The root item is ready. Associate it with the window. _rootItem = newItem; @@ -390,54 +393,62 @@ void OffscreenUi::setProxyWindow(QWindow* window) { _renderControl->_renderWindow = window; } -void OffscreenUi::show(const QUrl& url, const QString& name) { +void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { QQuickItem* item = _rootItem->findChild(name); // First load? if (!item) { - load(url); + load(url, f); return; } item->setEnabled(true); } -void OffscreenUi::toggle(const QUrl& url, const QString& name) { +void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function f) { QQuickItem* item = _rootItem->findChild(name); // First load? if (!item) { - load(url); + load(url, f); return; } item->setEnabled(!item->isEnabled()); } void OffscreenUi::messageBox(const QString& title, const QString& text, + ButtonCallback callback, QMessageBox::Icon icon, - QMessageBox::StandardButtons buttons, - ButtonCallback f) { + QMessageBox::StandardButtons buttons) { + MessageDialog::show([=](QQmlContext*ctx, QQuickItem*item) { + MessageDialog * pDialog = item->findChild(); + pDialog->setIcon((MessageDialog::Icon)icon); + pDialog->setTitle(title); + pDialog->setText(text); + pDialog->setStandardButtons(MessageDialog::StandardButtons((int)buttons)); + pDialog->setResultCallback(callback); + }); } void OffscreenUi::information(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons, - ButtonCallback callback) { - callback(QMessageBox::information(nullptr, title, text, buttons)); + ButtonCallback callback, + QMessageBox::StandardButtons buttons) { + messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Information, buttons); } void OffscreenUi::question(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons, - ButtonCallback callback) { - callback(QMessageBox::question(nullptr, title, text, buttons)); + ButtonCallback callback, + QMessageBox::StandardButtons buttons) { + messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Question, buttons); } void OffscreenUi::warning(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons, - ButtonCallback callback) { - callback(QMessageBox::warning(nullptr, title, text, buttons)); + ButtonCallback callback, + QMessageBox::StandardButtons buttons) { + messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Warning, buttons); } void OffscreenUi::critical(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons, - ButtonCallback callback) { - callback(QMessageBox::critical(nullptr, title, text, buttons)); + ButtonCallback callback, + QMessageBox::StandardButtons buttons) { + messageBox(title, text, callback, (QMessageBox::Icon)MessageDialog::Critical, buttons); } diff --git a/libraries/render-utils/src/OffscreenUi.h b/libraries/render-utils/src/OffscreenUi.h index c64d0d833c..b2805b2ded 100644 --- a/libraries/render-utils/src/OffscreenUi.h +++ b/libraries/render-utils/src/OffscreenUi.h @@ -59,12 +59,12 @@ public: virtual ~OffscreenUi(); void create(QOpenGLContext* context); void resize(const QSize& size); - void load(const QUrl& qmlSource, std::function f = [](QQmlContext*) {}); - void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*) {}) { + void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QQuickItem*) {}); + void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QQuickItem*) {}) { load(QUrl(qmlSourceFile), f); } - void show(const QUrl& url, const QString& name); - void toggle(const QUrl& url, const QString& name); + void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {})); + void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {})); void setBaseUrl(const QUrl& baseUrl); void addImportPath(const QString& path); QQmlContext* qmlContext(); @@ -86,31 +86,32 @@ public: static ButtonCallback NO_OP_CALLBACK; static void messageBox(const QString& title, const QString& text, + static void messageBox(const QString &title, const QString &text, + ButtonCallback f, QMessageBox::Icon icon, - QMessageBox::StandardButtons buttons, - ButtonCallback f); + QMessageBox::StandardButtons buttons); static void information(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - ButtonCallback callback = NO_OP_CALLBACK); + ButtonCallback callback = NO_OP_CALLBACK, + QMessageBox::StandardButtons buttons = QMessageBox::Ok); static void question(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No), - ButtonCallback callback = [](QMessageBox::StandardButton) {}); + ButtonCallback callback = NO_OP_CALLBACK, + QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No)); static void warning(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - ButtonCallback callback = [](QMessageBox::StandardButton) {}); + ButtonCallback callback = NO_OP_CALLBACK, + QMessageBox::StandardButtons buttons = QMessageBox::Ok); static void critical(const QString& title, const QString& text, - QMessageBox::StandardButtons buttons = QMessageBox::Ok, - ButtonCallback callback = [](QMessageBox::StandardButton) {}); + ButtonCallback callback = NO_OP_CALLBACK, + QMessageBox::StandardButtons buttons = QMessageBox::Ok); protected: private slots: void updateQuick(); - void finishQmlLoad(); + void finishQmlLoad(std::function f); public slots: void requestUpdate(); From 3ba0df520147f5e7ced5481766d0fb020a68a712 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 20 Apr 2015 18:41:49 -0700 Subject: [PATCH 03/33] Fixing headers --- interface/src/ui/MarketplaceDialog.cpp | 2 +- interface/src/ui/MarketplaceDialog.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp index ed30a2c120..4893d47103 100644 --- a/interface/src/ui/MarketplaceDialog.cpp +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -1,5 +1,5 @@ // -// AddressBarDialog.cpp +// MarketplaceDialog.cpp // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. diff --git a/interface/src/ui/MarketplaceDialog.h b/interface/src/ui/MarketplaceDialog.h index 137bb0bea7..f5fa355201 100644 --- a/interface/src/ui/MarketplaceDialog.h +++ b/interface/src/ui/MarketplaceDialog.h @@ -1,5 +1,5 @@ // -// AddressBarDialog.h +// MarketplaceDialog.h // // Created by Bradley Austin Davis on 2015/04/14 // Copyright 2015 High Fidelity, Inc. From d2ff89aaecd150388ac858409ba33aa83ababb69 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Apr 2015 14:21:59 -0700 Subject: [PATCH 04/33] Cleaning up qml --- interface/resources/qml/AddressBarDialog.qml | 10 +- interface/resources/qml/Browser.qml | 21 +- interface/resources/qml/CustomButton.qml | 27 -- interface/resources/qml/CustomTextArea.qml | 10 - interface/resources/qml/Icon.qml | 8 - interface/resources/qml/LoginDialog.qml | 29 +-- interface/resources/qml/MarketplaceDialog.qml | 5 +- interface/resources/qml/MenuTest.qml | 12 + interface/resources/qml/MessageDialog.qml | 41 +-- interface/resources/qml/Palettes.qml | 86 ------- interface/resources/qml/Root.qml | 5 +- interface/resources/qml/TestDialog.qml | 22 +- interface/resources/qml/TestRoot.qml | 28 +- interface/resources/qml/controls/Button.qml | 10 + .../{CustomDialog.qml => controls/Dialog.qml} | 242 ++++++++++-------- .../IconButton.qml} | 0 .../resources/qml/controls/MenuButton.qml | 9 + interface/resources/qml/controls/README.md | 2 + .../{CustomTextEdit.qml => controls/Text.qml} | 4 +- .../{CustomText.qml => controls/TextArea.qml} | 4 +- interface/resources/qml/controls/TextEdit.qml | 7 + .../TextInput.qml} | 4 +- interface/resources/qml/hifiConstants.js | 4 - .../{CustomBorder.qml => styles/Border.qml} | 1 - .../resources/qml/styles/ButtonStyle.qml | 24 ++ .../resources/qml/styles/HifiPalette.qml | 5 + .../resources/qml/styles/IconButtonStyle.qml | 15 ++ .../resources/qml/styles/MenuButtonStyle.qml | 24 ++ 28 files changed, 325 insertions(+), 334 deletions(-) delete mode 100644 interface/resources/qml/CustomButton.qml delete mode 100644 interface/resources/qml/CustomTextArea.qml delete mode 100644 interface/resources/qml/Icon.qml create mode 100644 interface/resources/qml/MenuTest.qml create mode 100644 interface/resources/qml/controls/Button.qml rename interface/resources/qml/{CustomDialog.qml => controls/Dialog.qml} (56%) rename interface/resources/qml/{IconControl.qml => controls/IconButton.qml} (100%) create mode 100644 interface/resources/qml/controls/MenuButton.qml create mode 100644 interface/resources/qml/controls/README.md rename interface/resources/qml/{CustomTextEdit.qml => controls/Text.qml} (54%) rename interface/resources/qml/{CustomText.qml => controls/TextArea.qml} (52%) create mode 100644 interface/resources/qml/controls/TextEdit.qml rename interface/resources/qml/{CustomTextInput.qml => controls/TextInput.qml} (90%) delete mode 100644 interface/resources/qml/hifiConstants.js rename interface/resources/qml/{CustomBorder.qml => styles/Border.qml} (99%) create mode 100644 interface/resources/qml/styles/ButtonStyle.qml create mode 100644 interface/resources/qml/styles/HifiPalette.qml create mode 100644 interface/resources/qml/styles/IconButtonStyle.qml create mode 100644 interface/resources/qml/styles/MenuButtonStyle.qml diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index ec1480928a..4c2631aa6d 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -1,10 +1,8 @@ import Hifi 1.0 import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Controls.Styles 1.3 +import "controls" -CustomDialog { +Dialog { title: "Go to..." objectName: "AddressBarDialog" height: 128 @@ -36,14 +34,14 @@ CustomDialog { anchors.margins: parent.margins anchors.topMargin: parent.topMargin - CustomBorder { + Border { height: 64 anchors.left: parent.left anchors.leftMargin: 0 anchors.right: goButton.left anchors.rightMargin: 8 anchors.verticalCenter: parent.verticalCenter - CustomTextInput { + TextInput { id: addressLine anchors.fill: parent helperText: "domain, location, @user, /x,y,z" diff --git a/interface/resources/qml/Browser.qml b/interface/resources/qml/Browser.qml index d7e08fbd97..a439f9114c 100644 --- a/interface/resources/qml/Browser.qml +++ b/interface/resources/qml/Browser.qml @@ -1,12 +1,10 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 -import QtQuick.Controls.Styles 1.3 import QtWebKit 3.0 +import "controls" -CustomDialog { - title: "Test Dlg" +Dialog { + title: "Browser Window" id: testDialog objectName: "Browser" width: 1280 @@ -18,7 +16,6 @@ CustomDialog { anchors.fill: parent anchors.margins: parent.margins anchors.topMargin: parent.topMargin - ScrollView { anchors.fill: parent @@ -30,16 +27,4 @@ CustomDialog { } } - - } - - -/* - -// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property - -MouseArea { - anchors.fill: parent -} -*/ diff --git a/interface/resources/qml/CustomButton.qml b/interface/resources/qml/CustomButton.qml deleted file mode 100644 index fb1b544207..0000000000 --- a/interface/resources/qml/CustomButton.qml +++ /dev/null @@ -1,27 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.3 -import QtQuick.Window 2.2 -import QtQuick.Controls.Styles 1.3 - -Button { - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } - text: "Text" - style: ButtonStyle { - padding { - top: 8 - left: 12 - right: 12 - bottom: 8 - } - background: CustomBorder { - anchors.fill: parent - } - label: CustomText { - renderType: Text.NativeRendering - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - text: control.text - color: control.enabled ? myPalette.text : myPalette.dark - } - } -} diff --git a/interface/resources/qml/CustomTextArea.qml b/interface/resources/qml/CustomTextArea.qml deleted file mode 100644 index cf3308e2b7..0000000000 --- a/interface/resources/qml/CustomTextArea.qml +++ /dev/null @@ -1,10 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -TextArea { - font.family: "Helvetica" - font.pointSize: 18 - backgroundVisible: false - readOnly: true -} - diff --git a/interface/resources/qml/Icon.qml b/interface/resources/qml/Icon.qml deleted file mode 100644 index 0d60afb2b7..0000000000 --- a/interface/resources/qml/Icon.qml +++ /dev/null @@ -1,8 +0,0 @@ -import QtQuick 1.0 - -Image { -id: icon -width: 64 -height: 64 -source: "file.svg" -} \ No newline at end of file diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index be69b65ef7..22162e323f 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -1,12 +1,11 @@ import Hifi 1.0 import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 import QtQuick.Controls.Styles 1.3 -import "hifiConstants.js" as HifiConstants +import "controls" -CustomDialog { +Dialog { title: "Login" + HifiPalette { id: hifiPalette } SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } objectName: "LoginDialog" height: 512 @@ -50,11 +49,11 @@ CustomDialog { source: "../images/hifi-logo.svg" } - CustomBorder { + Border { width: 304 height: 64 anchors.horizontalCenter: parent.horizontalCenter - CustomTextInput { + TextInput { id: username anchors.fill: parent helperText: "Username or Email" @@ -67,11 +66,11 @@ CustomDialog { } } - CustomBorder { + Border { width: 304 height: 64 anchors.horizontalCenter: parent.horizontalCenter - CustomTextInput { + TextInput { id: password anchors.fill: parent echoMode: TextInput.Password @@ -94,7 +93,7 @@ CustomDialog { } } - CustomText { + Text { anchors.horizontalCenter: parent.horizontalCenter textFormat: Text.StyledText width: parent.width @@ -117,7 +116,7 @@ CustomDialog { width: 192 height: 64 anchors.horizontalCenter: parent.horizontalCenter - color: HifiConstants.color + color: hifiPalette.hifiBlue border.width: 0 radius: 10 @@ -142,7 +141,7 @@ CustomDialog { width: 32 source: "../images/login.svg" } - CustomText { + Text { text: "Login" color: "white" width: 64 @@ -152,7 +151,7 @@ CustomDialog { } - CustomText { + Text { width: parent.width height: 24 horizontalAlignment: Text.AlignHCenter @@ -160,7 +159,7 @@ CustomDialog { text:"Create Account" font.pointSize: 12 font.bold: true - color: HifiConstants.color + color: hifiPalette.hifiBlue MouseArea { anchors.fill: parent @@ -170,14 +169,14 @@ CustomDialog { } } - CustomText { + Text { width: parent.width height: 24 horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter font.pointSize: 12 text: "Recover Password" - color: HifiConstants.color + color: hifiPalette.hifiBlue MouseArea { anchors.fill: parent diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index 3fdd9b2b4d..fe192179eb 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -1,12 +1,11 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 import QtQuick.Controls.Styles 1.3 import QtWebKit 3.0 +import "controls" -CustomDialog { +Dialog { title: "Test Dlg" id: testDialog objectName: "Browser" diff --git a/interface/resources/qml/MenuTest.qml b/interface/resources/qml/MenuTest.qml new file mode 100644 index 0000000000..13fc997791 --- /dev/null +++ b/interface/resources/qml/MenuTest.qml @@ -0,0 +1,12 @@ +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Styles 1.3 + +Item { + anchors.fill: parent + + Rectangle { + anchors.fill: parent + color: "red" + } +} diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index f4a360baed..c7c1b23223 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -43,8 +43,9 @@ import QtQuick 2.2 import QtQuick.Controls 1.2 import QtQuick.Window 2.1 import QtQuick.Dialogs 1.2 +import "controls" -CustomDialog { +Dialog { id: root property real spacing: 8 property real outerSpacing: 16 @@ -179,115 +180,115 @@ CustomDialog { spacing: root.spacing layoutDirection: Qt.RightToLeft width: parent.width - CustomButton { + Button { id: okButton text: qsTr("OK") onClicked: content.click(StandardButton.Ok) visible: content.standardButtons & StandardButton.Ok } - CustomButton { + Button { id: openButton text: qsTr("Open") onClicked: content.click(StandardButton.Open) visible: content.standardButtons & StandardButton.Open } - CustomButton { + Button { id: saveButton text: qsTr("Save") onClicked: content.click(StandardButton.Save) visible: content.standardButtons & StandardButton.Save } - CustomButton { + Button { id: saveAllButton text: qsTr("Save All") onClicked: content.click(StandardButton.SaveAll) visible: content.standardButtons & StandardButton.SaveAll } - CustomButton { + Button { id: retryButton text: qsTr("Retry") onClicked: content.click(StandardButton.Retry) visible: content.standardButtons & StandardButton.Retry } - CustomButton { + Button { id: ignoreButton text: qsTr("Ignore") onClicked: content.click(StandardButton.Ignore) visible: content.standardButtons & StandardButton.Ignore } - CustomButton { + Button { id: applyButton text: qsTr("Apply") onClicked: content.click(StandardButton.Apply) visible: content.standardButtons & StandardButton.Apply } - CustomButton { + Button { id: yesButton text: qsTr("Yes") onClicked: content.click(StandardButton.Yes) visible: content.standardButtons & StandardButton.Yes } - CustomButton { + Button { id: yesAllButton text: qsTr("Yes to All") onClicked: content.click(StandardButton.YesToAll) visible: content.standardButtons & StandardButton.YesToAll } - CustomButton { + Button { id: noButton text: qsTr("No") onClicked: content.click(StandardButton.No) visible: content.standardButtons & StandardButton.No } - CustomButton { + Button { id: noAllButton text: qsTr("No to All") onClicked: content.click(StandardButton.NoToAll) visible: content.standardButtons & StandardButton.NoToAll } - CustomButton { + Button { id: discardButton text: qsTr("Discard") onClicked: content.click(StandardButton.Discard) visible: content.standardButtons & StandardButton.Discard } - CustomButton { + Button { id: resetButton text: qsTr("Reset") onClicked: content.click(StandardButton.Reset) visible: content.standardButtons & StandardButton.Reset } - CustomButton { + Button { id: restoreDefaultsButton text: qsTr("Restore Defaults") onClicked: content.click(StandardButton.RestoreDefaults) visible: content.standardButtons & StandardButton.RestoreDefaults } - CustomButton { + Button { id: cancelButton text: qsTr("Cancel") onClicked: content.click(StandardButton.Cancel) visible: content.standardButtons & StandardButton.Cancel } - CustomButton { + Button { id: abortButton text: qsTr("Abort") onClicked: content.click(StandardButton.Abort) visible: content.standardButtons & StandardButton.Abort } - CustomButton { + Button { id: closeButton text: qsTr("Close") onClicked: content.click(StandardButton.Close) visible: content.standardButtons & StandardButton.Close } - CustomButton { + Button { id: moreButton text: qsTr("Show Details...") onClicked: content.state = (content.state === "" ? "expanded" : "") visible: content.detailedText.length > 0 } - CustomButton { + Button { id: helpButton text: qsTr("Help") onClicked: content.click(StandardButton.Help) diff --git a/interface/resources/qml/Palettes.qml b/interface/resources/qml/Palettes.qml index c4b0953df7..2bdf6eba8b 100644 --- a/interface/resources/qml/Palettes.qml +++ b/interface/resources/qml/Palettes.qml @@ -1,8 +1,5 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 -import QtQuick.Controls.Styles 1.3 Rectangle { color: "teal" @@ -150,87 +147,4 @@ Rectangle { Rectangle { height: parent.height; width: 16; color: spd.highlightedText} } } - - -/* - CustomDialog { - title: "Test Dlg" - anchors.fill: parent - - Rectangle { - property int d: 100 - id: square - objectName: "testRect" - width: d - height: d - anchors.centerIn: parent - color: "red" - NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; } - } - - - CustomTextEdit { - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - clip: true - text: "test edit" - anchors.top: parent.top - anchors.topMargin: parent.titleSize + 12 - } - - CustomButton { - x: 128 - y: 192 - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - anchors.right: parent.right - anchors.rightMargin: 12 - onClicked: { - console.log("Click"); - if (square.visible) { - square.visible = false - } else { - square.visible = true - } - } - } - - CustomButton { - id: customButton2 - y: 192 - text: "Close" - anchors.left: parent.left - anchors.leftMargin: 12 - anchors.bottom: parent.bottom - anchors.bottomMargin: 12 - onClicked: { - onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0 - } - } - - Keys.onPressed: { - console.log("Key " + event.key); - switch (event.key) { - case Qt.Key_Q: - if (Qt.ControlModifier == event.modifiers) { - event.accepted = true; - break; - } - } - } - } -*/ - } - - -/* - -// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property - -MouseArea { - anchors.fill: parent -} -*/ diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 9422ef123d..b2db7d18bf 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -1,9 +1,10 @@ import Hifi 1.0 import QtQuick 2.3 +// This is our primary 'window' object to which all dialogs and controls will +// be childed. Root { id: root - width: 1280 - height: 720 + anchors.fill: parent } diff --git a/interface/resources/qml/TestDialog.qml b/interface/resources/qml/TestDialog.qml index 1fe8676bc6..15bd790c22 100644 --- a/interface/resources/qml/TestDialog.qml +++ b/interface/resources/qml/TestDialog.qml @@ -1,10 +1,9 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 import QtQuick.Controls.Styles 1.3 +import "controls" -CustomDialog { +Dialog { title: "Test Dialog" id: testDialog objectName: "TestDialog" @@ -37,7 +36,7 @@ CustomDialog { } - CustomTextEdit { + TextEdit { id: edit anchors.left: parent.left anchors.leftMargin: 12 @@ -49,7 +48,7 @@ CustomDialog { anchors.topMargin: 12 } - CustomButton { + Button { x: 128 y: 192 text: "Test" @@ -68,7 +67,7 @@ CustomDialog { } } - CustomButton { + Button { id: customButton2 y: 192 text: "Move" @@ -92,15 +91,4 @@ CustomDialog { } } } - } - - -/* - -// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property - -MouseArea { - anchors.fill: parent -} -*/ diff --git a/interface/resources/qml/TestRoot.qml b/interface/resources/qml/TestRoot.qml index 158c0b7a54..0f48939935 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -1,12 +1,26 @@ import Hifi 1.0 import QtQuick 2.3 +// Import local folder last so that our own control customizations override +// the built in ones +import "controls" Root { id: root - width: 1280 - height: 720 + anchors.fill: parent - CustomButton { + onWidthChanged: { + console.log("Root width: " + width) + } + onHeightChanged: { + console.log("Root height: " + height) + } + + Component.onCompleted: { + console.log("Completed root") + root.forceActiveFocus() + } + + Button { id: messageBox anchors.right: createDialog.left anchors.rightMargin: 24 @@ -20,7 +34,7 @@ Root { } } - CustomButton { + Button { id: createDialog anchors.right: parent.right anchors.rightMargin: 24 @@ -28,8 +42,12 @@ Root { anchors.bottomMargin: 24 text: "Create" onClicked: { - root.loadChild("TestDialog.qml"); + root.loadChild("MenuTest.qml"); } } + + Keys.onPressed: { + console.log(event.key); + } } diff --git a/interface/resources/qml/controls/Button.qml b/interface/resources/qml/controls/Button.qml new file mode 100644 index 0000000000..215e0542f7 --- /dev/null +++ b/interface/resources/qml/controls/Button.qml @@ -0,0 +1,10 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 +import "." +import "../styles" + +Original.Button { + style: ButtonStyle { + } +} diff --git a/interface/resources/qml/CustomDialog.qml b/interface/resources/qml/controls/Dialog.qml similarity index 56% rename from interface/resources/qml/CustomDialog.qml rename to interface/resources/qml/controls/Dialog.qml index 1e0351af4f..78642eaef4 100644 --- a/interface/resources/qml/CustomDialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -1,40 +1,78 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 -import QtQuick.Window 2.2 -import QtQuick.Dialogs 1.2 -import QtQuick.Controls.Styles 1.3 -import "hifiConstants.js" as HifiConstants +import "." +import "../styles" +/* + * FIXME Need to create a client property here so that objects can be + * placed in it without having to think about positioning within the outer + * window. + * + * Examine the QML ApplicationWindow.qml source for how it does this + * + */ Item { - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } - id: dialog - width: 256 - height: 256 - scale: 0.0 - enabled: false + id: root + + HifiPalette { id: hifiPalette } + SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } + property int animationDuration: 400 property bool destroyOnInvisible: false property bool destroyOnCloseButton: true property bool resizable: false property int minX: 256 property int minY: 256 - clip: true + 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 + enabled: false + scale: 0.0 + + /* + * Support for animating the dialog in and out. + */ + + // The offscreen UI will enable an object, rather than manipulating it's + // 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. onEnabledChanged: { scale = enabled ? 1.0 : 0.0 } - + + // The actual animator + Behavior on scale { + NumberAnimation { + duration: root.animationDuration + easing.type: Easing.InOutBounce + } + } + + // We remove any load the dialog might have on the QML by toggling it's + // visibility based on the state of the animated property onScaleChanged: { visible = (scale != 0.0); } + // Some dialogs should be destroyed when they become invisible, so handle that onVisibleChanged: { if (!visible && destroyOnInvisible) { - console.log("Destroying closed component"); destroy(); } } + // our close function performs the same way as the OffscreenUI class: + // don't do anything but manipulate the enabled flag and let the other + // mechanisms decide if the window should be destoryed after the close + // animation completes function close() { if (destroyOnCloseButton) { destroyOnInvisible = true @@ -42,102 +80,14 @@ Item { enabled = false; } + /* + * Resize support + */ function deltaSize(dx, dy) { width = Math.max(width + dx, minX) height = Math.max(height + dy, minY) } - Behavior on scale { - NumberAnimation { - //This specifies how long the animation takes - duration: dialog.animationDuration - //This selects an easing curve to interpolate with, the default is Easing.Linear - easing.type: Easing.InOutBounce - } - } - - property int topMargin: dialog.height - clientBorder.height + 8 - property int margins: 8 - property string title - property int titleSize: titleBorder.height + 12 - property string frameColor: HifiConstants.color - property string backgroundColor: myPalette.window - property string headerBackgroundColor: myPalette.dark - - CustomBorder { - id: windowBorder - anchors.fill: parent - border.color: dialog.frameColor - color: dialog.backgroundColor - - CustomBorder { - id: titleBorder - height: 48 - anchors.right: parent.right - anchors.rightMargin: 0 - anchors.left: parent.left - anchors.leftMargin: 0 - border.color: dialog.frameColor - color: dialog.headerBackgroundColor - - CustomText { - id: titleText - color: "white" - text: dialog.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: dialog - minimumX: 0 - minimumY: 0 - maximumX: dialog.parent ? dialog.parent.width - dialog.width : 0 - maximumY: dialog.parent ? dialog.parent.height - dialog.height : 0 - } - } - Image { - id: closeButton - x: 360 - height: 16 - anchors.verticalCenter: parent.verticalCenter - width: 16 - anchors.right: parent.right - anchors.rightMargin: 12 - source: "../styles/close.svg" - MouseArea { - anchors.fill: parent - onClicked: { - dialog.close(); - } - } - } - } // header border - - CustomBorder { - id: clientBorder - border.color: dialog.frameColor - 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 - MouseArea { id: sizeDrag property int startX @@ -152,11 +102,91 @@ Item { startY = mouseY } onPositionChanged: { - if (pressed && dialog.resizable) { - dialog.deltaSize((mouseX - startX), (mouseY - startY)) + if (pressed && root.resizable) { + root.deltaSize((mouseX - startX), (mouseY - startY)) startX = mouseX startY = mouseY } } } + + /* + * Window decorations, with a title bar and frames + */ + Border { + id: windowBorder + anchors.fill: parent + border.color: root.frameColor + color: root.backgroundColor + + Border { + id: titleBorder + height: 48 + 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.verticalCenter: parent.verticalCenter + width: 16 + anchors.right: parent.right + anchors.rightMargin: 12 + source: "../../styles/close.svg" + MouseArea { + 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 + } diff --git a/interface/resources/qml/IconControl.qml b/interface/resources/qml/controls/IconButton.qml similarity index 100% rename from interface/resources/qml/IconControl.qml rename to interface/resources/qml/controls/IconButton.qml diff --git a/interface/resources/qml/controls/MenuButton.qml b/interface/resources/qml/controls/MenuButton.qml new file mode 100644 index 0000000000..25ad4b2c48 --- /dev/null +++ b/interface/resources/qml/controls/MenuButton.qml @@ -0,0 +1,9 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 as Original +import QtQuick.Controls.Styles 1.3 +import "../styles" + +Original.Button { + style: MenuButtonStyle { + } +} diff --git a/interface/resources/qml/controls/README.md b/interface/resources/qml/controls/README.md new file mode 100644 index 0000000000..7f05f32a63 --- /dev/null +++ b/interface/resources/qml/controls/README.md @@ -0,0 +1,2 @@ +These are our own custom controls with the same names as existing controls, but +customized for readability / usability in VR. diff --git a/interface/resources/qml/CustomTextEdit.qml b/interface/resources/qml/controls/Text.qml similarity index 54% rename from interface/resources/qml/CustomTextEdit.qml rename to interface/resources/qml/controls/Text.qml index 0602bbc150..a9c19e70b4 100644 --- a/interface/resources/qml/CustomTextEdit.qml +++ b/interface/resources/qml/controls/Text.qml @@ -1,6 +1,6 @@ -import QtQuick 2.3 +import QtQuick 2.3 as Original -TextEdit { +Original.Text { font.family: "Helvetica" font.pointSize: 18 } diff --git a/interface/resources/qml/CustomText.qml b/interface/resources/qml/controls/TextArea.qml similarity index 52% rename from interface/resources/qml/CustomText.qml rename to interface/resources/qml/controls/TextArea.qml index 83229b783e..dfa177bcb6 100644 --- a/interface/resources/qml/CustomText.qml +++ b/interface/resources/qml/controls/TextArea.qml @@ -1,6 +1,6 @@ -import QtQuick 2.3 +import QtQuick 2.3 as Original -Text { +Original.TextArea { font.family: "Helvetica" font.pointSize: 18 } diff --git a/interface/resources/qml/controls/TextEdit.qml b/interface/resources/qml/controls/TextEdit.qml new file mode 100644 index 0000000000..28551bb171 --- /dev/null +++ b/interface/resources/qml/controls/TextEdit.qml @@ -0,0 +1,7 @@ +import QtQuick 2.3 as Original + +Original.TextEdit { + font.family: "Helvetica" + font.pointSize: 18 +} + diff --git a/interface/resources/qml/CustomTextInput.qml b/interface/resources/qml/controls/TextInput.qml similarity index 90% rename from interface/resources/qml/CustomTextInput.qml rename to interface/resources/qml/controls/TextInput.qml index a706187376..8ce3d85d81 100644 --- a/interface/resources/qml/CustomTextInput.qml +++ b/interface/resources/qml/controls/TextInput.qml @@ -3,7 +3,7 @@ import QtQuick.Controls 1.2 TextInput { SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } - property string helperText: "" + property string helperText font.family: "Helvetica" font.pointSize: 18 width: 256 @@ -24,7 +24,7 @@ TextInput { id: helperText anchors.fill: parent font.pointSize: parent.font.pointSize - font.family: "Helvetica" + font.family: parent.font.family verticalAlignment: TextInput.AlignVCenter text: parent.helperText color: myPalette.dark diff --git a/interface/resources/qml/hifiConstants.js b/interface/resources/qml/hifiConstants.js deleted file mode 100644 index 860226c963..0000000000 --- a/interface/resources/qml/hifiConstants.js +++ /dev/null @@ -1,4 +0,0 @@ -var color = "#0e7077" -var Colors = { - hifiBlue: "#0e7077" -} diff --git a/interface/resources/qml/CustomBorder.qml b/interface/resources/qml/styles/Border.qml similarity index 99% rename from interface/resources/qml/CustomBorder.qml rename to interface/resources/qml/styles/Border.qml index 1bb30d1ebc..7d38e7d277 100644 --- a/interface/resources/qml/CustomBorder.qml +++ b/interface/resources/qml/styles/Border.qml @@ -1,6 +1,5 @@ import QtQuick 2.3 - Rectangle { SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } property int margin: 5 diff --git a/interface/resources/qml/styles/ButtonStyle.qml b/interface/resources/qml/styles/ButtonStyle.qml new file mode 100644 index 0000000000..caf366364a --- /dev/null +++ b/interface/resources/qml/styles/ButtonStyle.qml @@ -0,0 +1,24 @@ +import QtQuick 2.4 as Original +import QtQuick.Controls.Styles 1.3 as OriginalStyles +import "." +import "../controls" + +OriginalStyles.ButtonStyle { + Original.SystemPalette { id: myPalette; colorGroup: Original.SystemPalette.Active } + padding { + top: 8 + left: 12 + right: 12 + bottom: 8 + } + background: Border { + 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 + } +} diff --git a/interface/resources/qml/styles/HifiPalette.qml b/interface/resources/qml/styles/HifiPalette.qml new file mode 100644 index 0000000000..641d533e3d --- /dev/null +++ b/interface/resources/qml/styles/HifiPalette.qml @@ -0,0 +1,5 @@ +import QtQuick 2.4 + +QtObject { + property string hifiBlue: "#0e7077" +} \ No newline at end of file diff --git a/interface/resources/qml/styles/IconButtonStyle.qml b/interface/resources/qml/styles/IconButtonStyle.qml new file mode 100644 index 0000000000..b341e5d6dd --- /dev/null +++ b/interface/resources/qml/styles/IconButtonStyle.qml @@ -0,0 +1,15 @@ +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/styles/MenuButtonStyle.qml b/interface/resources/qml/styles/MenuButtonStyle.qml new file mode 100644 index 0000000000..1dca89ffea --- /dev/null +++ b/interface/resources/qml/styles/MenuButtonStyle.qml @@ -0,0 +1,24 @@ +import "../controls" +import "." + +ButtonStyle { + SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } + padding { + top: 8 + left: 12 + right: 12 + bottom: 8 + } + background: Border { + anchors.fill: parent + color: "#00000000" + borderColor: "red" + } + label: Text { + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + text: control.text + color: control.enabled ? myPalette.text : myPalette.dark + } +} From 8cb298a55b9ed36ad161c21d50021aa97cc22707 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Apr 2015 18:11:42 -0700 Subject: [PATCH 05/33] Working on menus --- interface/CMakeLists.txt | 2 +- .../resources/fonts/fontawesome-webfont.ttf | Bin 0 -> 122092 bytes interface/resources/qml/AddressBarDialog.qml | 1 + interface/resources/qml/HifiAction.qml | 20 + interface/resources/qml/LoginDialog.qml | 1 + interface/resources/qml/Menu.qml | 375 +++++++++++++ interface/resources/qml/MenuTest.qml | 12 - interface/resources/qml/MessageDialog.qml | 1 - interface/resources/qml/controls/Dialog.qml | 5 +- .../resources/qml/controls/MenuButton.qml | 6 +- .../resources/qml/styles/MenuButtonStyle.qml | 20 +- interface/src/Application.cpp | 109 ++-- interface/src/Application.h | 6 +- interface/src/Bookmarks.cpp | 67 +-- interface/src/Bookmarks.h | 13 +- interface/src/Menu.cpp | 258 ++++++--- interface/src/Menu.h | 99 ++-- interface/src/devices/OculusManager.cpp | 2 +- interface/src/ui/HMDToolsDialog.cpp | 1 + interface/src/ui/LoginDialog.cpp | 17 +- interface/src/ui/MenuQml.cpp | 19 + interface/src/ui/MenuQml.h | 26 + interface/src/ui/RunningScriptsWidget.cpp | 8 +- interface/src/ui/ToolWindow.cpp | 3 +- libraries/ui/CMakeLists.txt | 12 + libraries/ui/src/MenuConstants.cpp | 50 ++ libraries/ui/src/MenuConstants.h | 503 ++++++++++++++++++ .../src/MessageDialog.cpp | 0 .../{render-utils => ui}/src/MessageDialog.h | 0 .../src/OffscreenQmlDialog.cpp | 0 .../src/OffscreenQmlDialog.h | 0 .../{render-utils => ui}/src/OffscreenUi.cpp | 12 +- .../{render-utils => ui}/src/OffscreenUi.h | 33 ++ tests/render-utils/src/main.cpp | 108 +--- tests/ui/CMakeLists.txt | 15 + tests/ui/src/main.cpp | 302 +++++++++++ 36 files changed, 1717 insertions(+), 389 deletions(-) create mode 100644 interface/resources/fonts/fontawesome-webfont.ttf create mode 100644 interface/resources/qml/HifiAction.qml create mode 100644 interface/resources/qml/Menu.qml delete mode 100644 interface/resources/qml/MenuTest.qml create mode 100644 interface/src/ui/MenuQml.cpp create mode 100644 interface/src/ui/MenuQml.h create mode 100644 libraries/ui/CMakeLists.txt create mode 100644 libraries/ui/src/MenuConstants.cpp create mode 100644 libraries/ui/src/MenuConstants.h rename libraries/{render-utils => ui}/src/MessageDialog.cpp (100%) rename libraries/{render-utils => ui}/src/MessageDialog.h (100%) rename libraries/{render-utils => ui}/src/OffscreenQmlDialog.cpp (100%) rename libraries/{render-utils => ui}/src/OffscreenQmlDialog.h (100%) rename libraries/{render-utils => ui}/src/OffscreenUi.cpp (99%) rename libraries/{render-utils => ui}/src/OffscreenUi.h (78%) create mode 100644 tests/ui/CMakeLists.txt create mode 100644 tests/ui/src/main.cpp diff --git a/interface/CMakeLists.txt b/interface/CMakeLists.txt index 7688036c94..b4e0e3a244 100644 --- a/interface/CMakeLists.txt +++ b/interface/CMakeLists.txt @@ -128,7 +128,7 @@ target_link_libraries(${TARGET_NAME} ${BULLET_LIBRARIES}) # link required hifi libraries link_hifi_libraries(shared octree environment gpu model fbx networking entities avatars audio audio-client animation script-engine physics - render-utils entities-renderer) + render-utils entities-renderer ui) add_dependency_external_projects(sdl2) diff --git a/interface/resources/fonts/fontawesome-webfont.ttf b/interface/resources/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..ed9372f8ea0fbaa04f42630a48887e4b38945345 GIT binary patch literal 122092 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 literal 0 HcmV?d00001 diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 4c2631aa6d..d12737081e 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -1,6 +1,7 @@ import Hifi 1.0 import QtQuick 2.3 import "controls" +import "styles" Dialog { title: "Go to..." diff --git a/interface/resources/qml/HifiAction.qml b/interface/resources/qml/HifiAction.qml new file mode 100644 index 0000000000..8e6e7d59b4 --- /dev/null +++ b/interface/resources/qml/HifiAction.qml @@ -0,0 +1,20 @@ +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/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 22162e323f..b3b926bbe3 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -2,6 +2,7 @@ import Hifi 1.0 import QtQuick 2.3 import QtQuick.Controls.Styles 1.3 import "controls" +import "styles" Dialog { title: "Login" diff --git a/interface/resources/qml/Menu.qml b/interface/resources/qml/Menu.qml new file mode 100644 index 0000000000..f76b9485fb --- /dev/null +++ b/interface/resources/qml/Menu.qml @@ -0,0 +1,375 @@ +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.Menu { + id: root + anchors.fill: parent + objectName: "Menu" + enabled: false + opacity: 0.0 + property int animationDuration: 200 + HifiPalette { id: hifiPalette } + + onEnabledChanged: { + if (enabled && columns.length == 0) { + pushColumn(menu.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 + property var source + property var root + property var listViewIndex + property var listView + text: typedText() + height: implicitHeight + width: implicitWidth + color: source.enabled ? "black" : "gray" + + onImplicitWidthChanged: { + width = implicitWidth + if (listView) { + listView.minWidth = Math.max(listView.minWidth, implicitWidth); + listView.recalculateSize(); + } + } + + onImplicitHeightChanged: { + height = implicitHeight + } + + 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.fill: parent + onClicked: { + listView.currentIndex = listViewIndex + parent.root.selectItem(parent.source); + } + } + } + } + + + property var menuBuilder: Component { + Border { + SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } + x: root.models.length * 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: 0; height: 32 + color: sysPalette.highlight + y: (listView.currentItem) ? listView.currentItem.y : 0; + Behavior on y { + NumberAnimation { + duration: 100 + easing.type: Easing.InOutQuint + } + } + } + Keys.onPressed: { + switch (event.key) { + case Qt.Key_Escape: + console.log("Backing out") + root.popColumn() + event.accepted = true; + } + } + + + 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 > 1) { + var curColumn = columns.pop(); + console.log(curColumn); + curColumn.visible = false; + curColumn.destroy(); + models.pop(); + curColumn = lastColumn(); + curColumn.enabled = true; + curColumn.opacity = 1.0; + curColumn.forceActiveFocus(); + } else { + enabled = false; + } + } + + function selectItem(source) { + switch (source.type) { + case 2: + pushColumn(source.items) + break; + case 1: + console.log("Triggering " + source.text); + source.trigger() + enabled = false + break; + case 0: + break; + } + } + + function reset() { + console.log("Resettting") + while (columns.length > 1) { + popColumn(); + } + lastColumn().children[0].currentIndex = -1 + console.log(lastColumn().children[0]) + } + + /* + HifiMenu { + id: rootMenu + Menu { + id: menu + Menu { + title: "File" + MenuItem { + action: HifiAction { + name: rootMenu.login + } + } + MenuItem { + action: HifiAction { + name: "Test" + checkable: true + } + } + MenuItem { + action: HifiAction { + name: rootMenu.quit + } + } + } + Menu { + title: "Edit" + MenuItem { + action: HifiAction { + text: "Copy" + shortcut: StandardKey.Copy + } + } + MenuItem { + action: HifiAction { + text: "Cut" + shortcut: StandardKey.Cut + } + } + MenuItem { + action: HifiAction { + text: "Paste" + shortcut: StandardKey.Paste + } + } + MenuItem { + action: HifiAction { + text: "Undo" + shortcut: StandardKey.Undo + } + } + MenuItem { + action: HifiAction { + text: "Redo" + shortcut: StandardKey.Redo + } + } + MenuItem { + action: HifiAction { + name: rootMenu.attachments + } + } + MenuItem { + action: HifiAction { + name: rootMenu.animations + } + } + } + + Menu { + title: "Scripts" + MenuItem { + action: HifiAction { + name: rootMenu.scriptEditor + } + } + MenuItem { + action: HifiAction { + name: rootMenu.loadScript + } + } + MenuItem { + action: HifiAction { + name: rootMenu.loadScriptURL + } + } + MenuItem { + action: HifiAction { + name: rootMenu.stopAllScripts + } + } + MenuItem { + action: HifiAction { + name: rootMenu.reloadAllScripts + } + } + MenuItem { + action: HifiAction { + name: rootMenu.runningScripts + } + } + } + Menu { + title: "Location" + MenuItem { + action: HifiAction { + name: rootMenu.addressBar + } + } + MenuItem { + action: HifiAction { + name: rootMenu.copyAddress + } + } + MenuItem { + action: HifiAction { + name: rootMenu.copyPath + } + } + } + } + } + */ + + MouseArea { + anchors.fill: parent + id: mouseArea + acceptedButtons: Qt.RightButton + onClicked: { + root.popColumn(); + } + } +} diff --git a/interface/resources/qml/MenuTest.qml b/interface/resources/qml/MenuTest.qml deleted file mode 100644 index 13fc997791..0000000000 --- a/interface/resources/qml/MenuTest.qml +++ /dev/null @@ -1,12 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Controls.Styles 1.3 - -Item { - anchors.fill: parent - - Rectangle { - anchors.fill: parent - color: "red" - } -} diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index c7c1b23223..eaf304b16a 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -41,7 +41,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.2 import QtQuick.Controls 1.2 -import QtQuick.Window 2.1 import QtQuick.Dialogs 1.2 import "controls" diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 78642eaef4..109889f738 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -30,14 +30,13 @@ Item { property string frameColor: hifiPalette.hifiBlue property string backgroundColor: sysPalette.window property string headerBackgroundColor: sysPalette.dark - clip: true - enabled: false - scale: 0.0 /* * Support for animating the dialog in and out. */ + enabled: false + scale: 0.0 // The offscreen UI will enable an object, rather than manipulating it's // visibility, so that we can do animations in both directions. Because diff --git a/interface/resources/qml/controls/MenuButton.qml b/interface/resources/qml/controls/MenuButton.qml index 25ad4b2c48..740995a199 100644 --- a/interface/resources/qml/controls/MenuButton.qml +++ b/interface/resources/qml/controls/MenuButton.qml @@ -1,9 +1,5 @@ import QtQuick 2.3 import QtQuick.Controls 1.3 as Original -import QtQuick.Controls.Styles 1.3 import "../styles" +import "../controls" -Original.Button { - style: MenuButtonStyle { - } -} diff --git a/interface/resources/qml/styles/MenuButtonStyle.qml b/interface/resources/qml/styles/MenuButtonStyle.qml index 1dca89ffea..f30ade3d33 100644 --- a/interface/resources/qml/styles/MenuButtonStyle.qml +++ b/interface/resources/qml/styles/MenuButtonStyle.qml @@ -1,24 +1,22 @@ +import QtQuick 2.4 +import QtQuick.Controls.Styles 1.3 import "../controls" import "." ButtonStyle { - SystemPalette { id: myPalette; colorGroup: SystemPalette.Active } + HifiPalette { id: hifiPalette } padding { - top: 8 - left: 12 - right: 12 - bottom: 8 + top: 2 + left: 4 + right: 4 + bottom: 2 } - background: Border { - anchors.fill: parent - color: "#00000000" - borderColor: "red" - } + background: Item {} label: Text { renderType: Text.NativeRendering verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: control.text - color: control.enabled ? myPalette.text : myPalette.dark + color: control.enabled ? "yellow" : "brown" } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b7ba384a97..c435ca2f0b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -149,6 +149,23 @@ extern "C" { } #endif +enum CustomEventTypes { + Lambda = QEvent::User + 1 +}; + +class LambdaEvent : public QEvent { + std::function _fun; +public: + LambdaEvent(const std::function & fun) : + QEvent(static_cast(Lambda)), _fun(fun) { + } + LambdaEvent(std::function && fun) : + QEvent(static_cast(Lambda)), _fun(fun) { + } + void call() { _fun(); } +}; + + using namespace std; // Starfield information @@ -345,9 +362,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 @@ -497,8 +511,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); - _fullscreenMenuWidget->setParent(_glWidget); - _menuBarHeight = Menu::getInstance()->height(); if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { setFullscreen(true); // Initialize menu bar show/hide } @@ -712,6 +724,13 @@ void Application::initializeGL() { initDisplay(); qCDebug(interfaceapp, "Initialized Display."); + // The UI can't be created until the primary OpenGL + // context is created, because it needs to share + // texture resources + initializeUi(); + qCDebug(interfaceapp, "Initialized Offscreen UI."); + _glWidget->makeCurrent(); + init(); qCDebug(interfaceapp, "init() complete."); @@ -740,11 +759,6 @@ void Application::initializeGL() { // update before the first render update(1.0f / _fps); - // The UI can't be created until the primary OpenGL - // context is created, because it needs to share - // texture resources - initializeUi(); - InfoView::showFirstTime(INFO_HELP_PATH); } @@ -764,6 +778,7 @@ void Application::initializeUi() { LoginDialog::registerType(); MarketplaceDialog::registerType(); MessageDialog::registerType(); + Menu::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -898,9 +913,7 @@ void Application::runTests() { } void Application::audioMuteToggled() { - QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio); - Q_CHECK_PTR(muteAction); - muteAction->setChecked(DependencyManager::get()->isMuted()); + Menu::getInstance()->setIsOptionChecked(MenuOption::MuteAudio, DependencyManager::get()->isMuted()); } void Application::aboutApp() { @@ -982,6 +995,10 @@ bool Application::importSVOFromURL(const QString& urlString) { bool Application::event(QEvent* event) { switch (event->type()) { + case Lambda: + ((LambdaEvent*)event)->call(); + return true; + case QEvent::MouseMove: mouseMoveEvent((QMouseEvent*)event); return true; @@ -1061,8 +1078,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) { return false; } -void Application::keyPressEvent(QKeyEvent* event) { +static bool _altPressed; +void Application::keyPressEvent(QKeyEvent* event) { + _altPressed = event->key() == Qt::Key_Alt; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1314,6 +1333,9 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { + if (event->key() == Qt::Key_Alt && _altPressed) { + Menu::toggle(); + } _keysPressed.remove(event->key()); @@ -1420,18 +1442,6 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } - if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) - && !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { - // Show/hide menu bar in fullscreen - if (event->globalY() > _menuBarHeight) { - _fullscreenMenuWidget->setFixedHeight(0); - Menu::getInstance()->setFixedHeight(0); - } else { - _fullscreenMenuWidget->setFixedHeight(_menuBarHeight); - Menu::getInstance()->setFixedHeight(_menuBarHeight); - } - } - _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1439,7 +1449,6 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { if (_controllerScriptingInterface.isMouseCaptured()) { return; } - } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1724,34 +1733,6 @@ void Application::setFullscreen(bool fullscreen) { Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); } -// The following code block is useful on platforms that can have a visible -// app menu in a fullscreen window. However the OSX mechanism hides the -// application menu for fullscreen apps, so the check is not required. -#ifndef Q_OS_MAC - if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { - if (fullscreen) { - // Menu hide() disables menu commands, and show() after hide() doesn't work with Rift VR display. - // So set height instead. - _window->menuBar()->setMaximumHeight(0); - } else { - _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); - } - } else { - if (fullscreen) { - // Move menu to a QWidget floating above _glWidget so that show/hide doesn't adjust viewport. - _menuBarHeight = Menu::getInstance()->height(); - Menu::getInstance()->setParent(_fullscreenMenuWidget); - Menu::getInstance()->setFixedWidth(_window->windowHandle()->screen()->size().width()); - _fullscreenMenuWidget->show(); - } else { - // Restore menu to being part of MainWindow. - _fullscreenMenuWidget->hide(); - _window->setMenuBar(Menu::getInstance()); - _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); - } - } -#endif - // Work around Qt bug that prevents floating menus being shown when in fullscreen mode. // https://bugreports.qt.io/browse/QTBUG-41883 // Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around. @@ -2004,16 +1985,18 @@ void Application::init() { OculusManager::connect(); if (OculusManager::isConnected()) { - QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), - "trigger", - Qt::QueuedConnection); + // perform as a post-event so that the code is run after init is complete + qApp->postLambdaEvent([] { + Menu::getInstance()->triggerOption(MenuOption::Fullscreen); + }); } TV3DManager::connect(); if (TV3DManager::isConnected()) { - QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), - "trigger", - Qt::QueuedConnection); + // perform as a post-event so that the code is run after init is complete + qApp->postLambdaEvent([] { + Menu::getInstance()->triggerOption(MenuOption::Fullscreen); + }); } _timerStart.start(); @@ -4434,3 +4417,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/Application.h b/interface/src/Application.h index 89de18dde5..f63f548c47 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -147,6 +148,8 @@ public: Application(int& argc, char** argv, QElapsedTimer &startup_time); ~Application(); + void postLambdaEvent(std::function f); + void loadScripts(); QString getPreviousScriptLocation(); void setPreviousScriptLocation(const QString& previousScriptLocation); @@ -620,9 +623,6 @@ private: void checkSkeleton(); - QWidget* _fullscreenMenuWidget = new QWidget(); - int _menuBarHeight; - QHash _acceptedExtensions; QList _domainConnectionRefusals; diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 4f022623dc..5af8234c7f 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -84,13 +84,16 @@ void Bookmarks::persistToFile() { saveFile.write(data); } -void Bookmarks::setupMenus(Menu* menubar, QMenu* menu) { +void Bookmarks::setupMenus(const QString & parentMenu) { // Add menus/actions - menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0, - this, SLOT(bookmarkLocation())); - _bookmarksMenu = menu->addMenu(MenuOption::Bookmarks); - _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark, 0, - this, SLOT(deleteBookmark())); + Menu * menu = Menu::getInstance(); + menu->addMenuItem(parentMenu, MenuOption::BookmarkLocation, [this] { + bookmarkLocation(); + }); + menu->addMenu(parentMenu, MenuOption::Bookmarks); + menu->addMenuItem(parentMenu, MenuOption::DeleteBookmark, [this] { + deleteBookmark(); + }); // Enable/Disable menus as needed enableMenuItems(_bookmarks.count() > 0); @@ -99,7 +102,7 @@ void Bookmarks::setupMenus(Menu* menubar, QMenu* menu) { for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) { QString bookmarkName = it.key(); QString bookmarkAddress = it.value().toString(); - addLocationToMenu(menubar, bookmarkName, bookmarkAddress); + addLocationToMenu(bookmarkName, bookmarkAddress); } } @@ -134,29 +137,20 @@ void Bookmarks::bookmarkLocation() { if (duplicateBookmarkMessage.exec() == QMessageBox::No) { return; } - removeLocationFromMenu(menubar, bookmarkName); + removeLocationFromMenu(bookmarkName); } - addLocationToMenu(menubar, bookmarkName, bookmarkAddress); + addLocationToMenu(bookmarkName, bookmarkAddress); insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. enableMenuItems(true); } -void Bookmarks::teleportToBookmark() { - QAction* action = qobject_cast(sender()); - QString address = action->data().toString(); - DependencyManager::get()->handleLookupString(address); -} - void Bookmarks::deleteBookmark() { - QStringList bookmarkList; - QList menuItems = _bookmarksMenu->actions(); - for (int i = 0; i < menuItems.count(); i += 1) { - bookmarkList.append(menuItems[i]->text()); - } - + bookmarkList.append(_bookmarks.keys()); + + QInputDialog deleteBookmarkDialog(qApp->getWindow()); deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); @@ -174,34 +168,29 @@ void Bookmarks::deleteBookmark() { return; } - removeLocationFromMenu(Menu::getInstance(), bookmarkName); + removeLocationFromMenu(bookmarkName); remove(bookmarkName); - - if (_bookmarksMenu->actions().count() == 0) { + + if (_bookmarks.keys().isEmpty()) { enableMenuItems(false); } } void Bookmarks::enableMenuItems(bool enabled) { - if (_bookmarksMenu) { - _bookmarksMenu->setEnabled(enabled); - } - if (_deleteBookmarksAction) { - _deleteBookmarksAction->setEnabled(enabled); - } + Menu* menu = Menu::getInstance(); + menu->enableMenuItem(MenuOption::Bookmarks, enabled); + menu->enableMenuItem(MenuOption::DeleteBookmark, enabled); } -void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { - QAction* teleportAction = new QAction(_bookmarksMenu); - teleportAction->setData(address); - connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); - - menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, - name, 0, QAction::NoRole); +void Bookmarks::addLocationToMenu(QString& name, QString& address) { + + Menu::getInstance()->addMenuItem(MenuOption::Bookmarks, name, [=] { + DependencyManager::get()->handleLookupString(address); + }); } -void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) { - menubar->removeAction(_bookmarksMenu, name); +void Bookmarks::removeLocationFromMenu(QString& name) { + Menu::getInstance()->removeMenuItem(name); } diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index 59f9efb1b1..d996d7c0f0 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -26,19 +26,16 @@ class Bookmarks: public QObject { public: Bookmarks(); - void setupMenus(Menu* menubar, QMenu* menu); + void setupMenus(const QString & menu); private slots: void bookmarkLocation(); - void teleportToBookmark(); void deleteBookmark(); private: + // FIXME bookmarks should be more categorizable + // Can we leverage a system browser favorites API? QVariantMap _bookmarks; // { name: address, ... } - - QPointer _bookmarksMenu; - QPointer _deleteBookmarksAction; - const QString BOOKMARKS_FILENAME = "bookmarks.json"; QString _bookmarksFilename; @@ -50,8 +47,8 @@ private: void persistToFile(); void enableMenuItems(bool enabled); - void addLocationToMenu(Menu* menubar, QString& name, QString& address); - void removeLocationFromMenu(Menu* menubar, QString& name); + void addLocationToMenu(QString& name, QString& address); + void removeLocationFromMenu(QString& name); }; #endif // hifi_Bookmarks_h \ No newline at end of file diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 9b9e93a4c9..026af1442f 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,7 +21,7 @@ #include #include #include - +#include #include "Application.h" #include "AccountManager.h" #include "audio/AudioIOStatsRenderer.h" @@ -42,64 +43,176 @@ #include "Menu.h" -Menu* Menu::_instance = NULL; +// Proxy object to simplify porting over +HifiAction::HifiAction(const QString & menuOption) : _menuOption(menuOption) { +} + +//void HifiAction::setCheckable(bool) { +// Menu::getInstance()->set +// qFatal("Not implemented"); +//} + +void HifiAction::setChecked(bool) { + qFatal("Not implemented"); +} + +void HifiAction::setVisible(bool) { + qFatal("Not implemented"); +} + +const QString & HifiAction::shortcut() const { + qFatal("Not implemented"); + return ""; +} + +void HifiAction::setText(const QString &) { + qFatal("Not implemented"); +} + +void HifiAction::setTriggerAction(std::function f) { + qFatal("Not implemented"); +} + +void HifiAction::setToggleAction(std::function f) { + qFatal("Not implemented"); +} + +HIFI_QML_DEF(Menu) + + +Menu* Menu::_instance = nullptr; Menu* Menu::getInstance() { - static QMutex menuInstanceMutex; - - // lock the menu instance mutex to make sure we don't race and create two menus and crash - menuInstanceMutex.lock(); - + // Optimistic check for menu existence if (!_instance) { - qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); + static QMutex menuInstanceMutex; + withLock(menuInstanceMutex, [] { + if (!_instance) { + OffscreenUi * offscreenUi = DependencyManager::get().data(); + QQmlContext * qmlContext = offscreenUi->qmlContext(); + qmlContext->setContextProperty("foo", QVariant::fromValue(foo)); +// qmlContext->setContextProperty("presetsModel", QVariant::fromValue(dataList)); - _instance = new Menu(); + qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); + load([&](QQmlContext *, QQuickItem* item) { + _instance = dynamic_cast(item); + }); + if (!_instance) { + qFatal("Could not load menu QML"); + } else { + _instance->init(); + } + } + }); } - - menuInstanceMutex.unlock(); - return _instance; } -Menu::Menu() { - QMenu* fileMenu = addMenu("File"); +Menu::Menu(QQuickItem * parent) : QQuickItem(parent) { +} + +QObject * _rootMenu; +static const QString FILE_MENU{ "File" }; +static const QString EDIT_MENU{ "Edit" }; +static const QString TOOLS_MENU{ "Tools" }; +static const QString AVATAR_MENU{ "Avatar" }; + + +static const QString MENU_SUFFIX{ "__Menu" }; + +QObject * addMenu(QObject * parent, const QString & text) { + // FIXME add more checking here to ensure no name conflicts + QVariant returnedValue; + QMetaObject::invokeMethod(parent, "addMenu", + 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; + QMetaObject::invokeMethod(parent, "addItem", + Q_RETURN_ARG(QQuickMenuItem*, returnedValue), + Q_ARG(QString, text)); + QObject* result = reinterpret_cast(returnedValue); + if (result) { + result->setObjectName(text + MENU_SUFFIX); + } + return result; +} + +void Menu::init() { + _rootMenu = property("menu").value(); + QObject * fileMenu = ::addMenu(_rootMenu, FILE_MENU); -#ifdef Q_OS_MAC - addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); -#endif auto dialogsManager = DependencyManager::get(); AccountManager& accountManager = AccountManager::getInstance(); { - addActionToQMenuAndActionHash(fileMenu, MenuOption::Login); - + ::addItem(fileMenu, MenuOption::Login); + // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item connect(&accountManager, &AccountManager::profileChanged, - dialogsManager.data(), &DialogsManager::toggleLoginDialog); + dialogsManager.data(), &DialogsManager::toggleLoginDialog); connect(&accountManager, &AccountManager::logoutComplete, - dialogsManager.data(), &DialogsManager::toggleLoginDialog); + dialogsManager.data(), &DialogsManager::toggleLoginDialog); } - - addDisabledActionAndSeparator(fileMenu, "Scripts"); - addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, - qApp, SLOT(loadDialog())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, - Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, - qApp, SLOT(reloadAllScripts())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, - qApp, SLOT(toggleRunningScriptsWidget())); - addDisabledActionAndSeparator(fileMenu, "Location"); - qApp->getBookmarks()->setupMenus(this, fileMenu); + +#ifdef Q_OS_MAC + addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); +#endif + + { + static const QString SCRIPTS_MENU{ "Scripts" }; + addMenu(FILE_MENU, SCRIPTS_MENU); + //Qt::CTRL | Qt::Key_O + addMenuItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { + qApp->loadDialog(); + }); + //Qt::CTRL | Qt::SHIFT | Qt::Key_O + addMenuItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { + qApp->loadScriptURLDialog(); + }); + addMenuItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { + qApp->stopAllScripts(); + }); + //Qt::CTRL | Qt::Key_R, + addMenuItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { + qApp->reloadAllScripts(); + }); + // Qt::CTRL | Qt::Key_J, + addMenuItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { + qApp->toggleRunningScriptsWidget(); + }); + } + + { + static const QString LOCATION_MENU{ "Location" }; + addMenu(FILE_MENU, LOCATION_MENU); + qApp->getBookmarks()->setupMenus(LOCATION_MENU); + //Qt::CTRL | Qt::Key_L + addMenuItem(LOCATION_MENU, MenuOption::AddressBar, [=] { + auto dialogsManager = DependencyManager::get(); + dialogsManager->toggleAddressBar(); + }); + addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { + auto addressManager = DependencyManager::get(); + addressManager->copyAddress(); + }); + addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { + auto addressManager = DependencyManager::get(); + addressManager->copyPath(); + }); + } +#if 0 - addActionToQMenuAndActionHash(fileMenu, - MenuOption::AddressBar, - Qt::CTRL | Qt::Key_L, - dialogsManager.data(), - SLOT(toggleAddressBar())); - auto addressManager = DependencyManager::get(); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, addressManager.data(), SLOT(copyAddress())); addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, @@ -556,15 +669,17 @@ Menu::Menu() { QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp); connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); #endif +#endif } void Menu::loadSettings() { - scanMenuBar(&Menu::loadAction); +// scanMenuBar(&Menu::loadAction); } void Menu::saveSettings() { - scanMenuBar(&Menu::saveAction); +// scanMenuBar(&Menu::saveAction); } +#if 0 void Menu::loadAction(Settings& settings, QAction& action) { if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { @@ -708,36 +823,20 @@ void Menu::removeAction(QMenu* menu, const QString& actionName) { _actionHash.remove(actionName); } +#endif + void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { - if (thread() != QThread::currentThread()) { - QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); - return; - } - QAction* menu = _actionHash.value(menuOption); - if (menu) { - menu->setChecked(isChecked); - } } bool Menu::isOptionChecked(const QString& menuOption) const { - const QAction* menu = _actionHash.value(menuOption); - if (menu) { - return menu->isChecked(); - } return false; } void Menu::triggerOption(const QString& menuOption) { - QAction* action = _actionHash.value(menuOption); - if (action) { - action->trigger(); - } else { - qCDebug(interfaceapp) << "NULL Action for menuOption '" << menuOption << "'"; - } } +#if 0 + QAction* Menu::getActionForOption(const QString& menuOption) { return _actionHash.value(menuOption); } @@ -951,15 +1050,12 @@ void Menu::addMenuItem(const MenuItemProperties& properties) { QMenuBar::repaint(); } } +#endif -void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { - QMenu* menuObj = getMenu(menu); - if (menuObj) { - removeAction(menuObj, menuitem); - QMenuBar::repaint(); - } +void Menu::removeMenuItem(const QString& menuitem) { }; +#if 0 bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { QAction* menuItemAction = _actionHash.value(menuitem); if (menuItemAction) { @@ -967,3 +1063,29 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { } return false; }; + +#endif + +void Menu::setOptionText(const QString& menuitem, const QString& text) { +} + +void Menu::addMenu(const QString& parentMenu, const QString& text) { +} + +void Menu::addMenuItem(const QString& parentMenu, const QString& menuItem) { +} + +void Menu::addMenuItem(const QString& parentMenu, const QString& menuItem, std::function f) { + addMenuItem(parentMenu, menuItem); + setOptionTriggerAction(parentMenu, f); +} + +void Menu::enableMenuItem(const QString& menuItem, bool enable) { +} + +void Menu::setOptionTriggerAction(const QString& menuOption, std::function f) { + _triggerActions[menuOption] = f; +} +void Menu::setOptionToggleAction(const QString& menuOption, std::function f) { + _toggleActions[menuOption] = f; +} diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 5a162d31bc..04275fcb32 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -18,85 +18,69 @@ #include #include #include - +#include #include +#include + #include "DiscoverabilityManager.h" class Settings; -class Menu : public QMenuBar { - Q_OBJECT +// Proxy object to simplify porting over +class HifiAction { + const QString _menuOption; public: + HifiAction(const QString & menuOption); + void setCheckable(bool); + void setChecked(bool); + void setVisible(bool); + const QString & shortcut() const; + void setText(const QString &); + void setTriggerAction(std::function); + void setToggleAction(std::function); +}; + +class Menu : public QQuickItem { + Q_OBJECT + HIFI_QML_DECL +public: + Menu(QQuickItem * parent = 0); static Menu* getInstance(); void loadSettings(); void saveSettings(); - - QMenu* getMenu(const QString& menuName); - - void triggerOption(const QString& menuOption); - QAction* getActionForOption(const QString& menuOption); - - QAction* addActionToQMenuAndActionHash(QMenu* 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* 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); - -public slots: - QMenu* addMenu(const QString& menuName); + HifiAction * getActionForOption(const QString& menuOption) { + return new HifiAction(menuOption); + } +// QMenu* addMenu(const QString& menuName); void removeMenu(const QString& menuName); bool menuExists(const QString& menuName); void addSeparator(const QString& menuName, const QString& separatorName); void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); - void removeMenuItem(const QString& menuName, const QString& menuitem); + void removeMenuItem(const QString& menuitem); bool menuItemExists(const QString& menuName, const QString& menuitem); bool isOptionChecked(const QString& menuOption) const; void setIsOptionChecked(const QString& menuOption, bool isChecked); - + void triggerOption(const QString& menuOption); + void setOptionText(const QString& menuOption, const QString & text); + void setOptionTriggerAction(const QString& menuOption, std::function f); + void setOptionToggleAction(const QString& menuOption, std::function f); + void addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f); + void addMenuItem(const QString & parentMenu, const QString & menuOption); + void addMenu(const QString & parentMenu, const QString & menuOption); + void enableMenuItem(const QString & menuOption, bool enabled = true); + +private: + void init(); + private: static Menu* _instance; - Menu(); - typedef void(*settingsAction)(Settings&, QAction&); - static void loadAction(Settings& settings, QAction& action); - static void saveAction(Settings& settings, QAction& action); - void scanMenuBar(settingsAction modifySetting); - 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, - int menuItemLocation = UNSPECIFIED_POSITION); - - QAction* addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut = 0, - const bool checked = false, - const QObject* receiver = NULL, - 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* getMenuAction(const QString& menuName); - int findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem); - int positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition); - - QHash _actionHash; + QHash> _triggerActions; + QHash> _toggleActions; + }; namespace MenuOption { @@ -251,5 +235,4 @@ namespace MenuOption { const QString VisibleToNoOne = "No one"; const QString Wireframe = "Wireframe"; } - #endif // hifi_Menu_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 7d719873f4..043d5bf40d 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -216,7 +216,7 @@ void OculusManager::connect() { _isConnected = false; // we're definitely not in "VR mode" so tell the menu that - Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); + Menu::getInstance()->setIsOptionChecked(MenuOption::EnableVRMode, false); } } diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index 4a899a641e..cedb26fd9b 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -8,6 +8,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +#include #include #include diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 5726818b2d..b8a4d6006b 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -25,19 +25,18 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _root } void LoginDialog::toggleAction() { - AccountManager& accountManager = AccountManager::getInstance(); - QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login); - Q_CHECK_PTR(loginAction); - disconnect(loginAction, 0, 0, 0); - + AccountManager & accountManager = AccountManager::getInstance(); + Menu* menu = Menu::getInstance(); if (accountManager.isLoggedIn()) { // change the menu item to logout - loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername()); - connect(loginAction, &QAction::triggered, &accountManager, &AccountManager::logout); + menu->setOptionText(MenuOption::Login, "Logout " + accountManager.getAccountInfo().getUsername()); + menu->setOptionTriggerAction(MenuOption::Login, [] { + AccountManager::getInstance().logout(); + }); } else { // change the menu item to login - loginAction->setText("Login"); - connect(loginAction, &QAction::triggered, [] { + menu->setOptionText(MenuOption::Login, "Login"); + menu->setOptionTriggerAction(MenuOption::Login, [] { LoginDialog::show(); }); } diff --git a/interface/src/ui/MenuQml.cpp b/interface/src/ui/MenuQml.cpp new file mode 100644 index 0000000000..991b7e4574 --- /dev/null +++ b/interface/src/ui/MenuQml.cpp @@ -0,0 +1,19 @@ +// +// MenuQml.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 "MenuQml.h" + +QML_DIALOG_DEF(MenuQml) + +MenuQml::MenuQml(QQuickItem *parent) : QQuickItem(parent) { + auto menu = Menu::getInstance(); + +} diff --git a/interface/src/ui/MenuQml.h b/interface/src/ui/MenuQml.h new file mode 100644 index 0000000000..a007ec8735 --- /dev/null +++ b/interface/src/ui/MenuQml.h @@ -0,0 +1,26 @@ +// +// MenuQml.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_MenuQml_h +#define hifi_MenuQml_h + +#include + +class MenuQml : public QQuickItem +{ + Q_OBJECT + QML_DIALOG_DECL + +public: + MenuQml(QQuickItem *parent = 0); +}; + +#endif diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index f892deabb6..4c8d29d84c 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 + // 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); @@ -161,8 +162,7 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int menuBarHeight = Menu::getInstance()->geometry().height(); - int topMargin = titleBarHeight + menuBarHeight; + int topMargin = titleBarHeight; setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin, size().width(), parentWidget()->height() - topMargin); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 5888b83f4a..4edae4f2a4 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -38,8 +38,7 @@ bool ToolWindow::event(QEvent* event) { QRect mainGeometry = mainWindow->geometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int menuBarHeight = Menu::getInstance()->geometry().height(); - int topMargin = titleBarHeight + menuBarHeight; + int topMargin = titleBarHeight; _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, DEFAULT_WIDTH, mainGeometry.height() - topMargin); diff --git a/libraries/ui/CMakeLists.txt b/libraries/ui/CMakeLists.txt new file mode 100644 index 0000000000..36a0a1a846 --- /dev/null +++ b/libraries/ui/CMakeLists.txt @@ -0,0 +1,12 @@ +set(TARGET_NAME ui) + +# use setup_hifi_library macro to setup our project and link appropriate Qt modules +setup_hifi_library(OpenGL Network Qml Quick Script) + +link_hifi_libraries(render-utils shared) + +add_dependency_external_projects(glm) +find_package(GLM REQUIRED) +target_include_directories(${TARGET_NAME} PUBLIC ${GLM_INCLUDE_DIRS}) + + diff --git a/libraries/ui/src/MenuConstants.cpp b/libraries/ui/src/MenuConstants.cpp new file mode 100644 index 0000000000..eeaf7b456b --- /dev/null +++ b/libraries/ui/src/MenuConstants.cpp @@ -0,0 +1,50 @@ +#include "MenuConstants.h" +#include + +QML_DIALOG_DEF(HifiMenu) + +static bool init = false; + +HifiMenu::HifiMenu(QQuickItem * parent) : QQuickItem(parent) { + this->setEnabled(false); + qWarning() << "Setting up connection"; + connect(this, &HifiMenu::enabledChanged, this, [=]() { + if (init) { + return; + } + init = true; + foreach(QObject * action, findChildren(QRegularExpression(".*HifiAction"))) { + connect(action, SIGNAL(triggeredByName(QString)), this, SLOT(onTriggeredByName(QString))); + connect(action, SIGNAL(toggledByName(QString)), this, SLOT(onToggledByName(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 (triggerActions.count(name)) { + if (toggleActions.count(name)) { + QObject * action = findChild(name + "HifiAction"); + bool checked = action->property("checked").toBool(); + toggleActions[name](checked); + } + } +} + +QHash> HifiMenu::triggerActions; +QHash> HifiMenu::toggleActions; + +void HifiMenu::setToggleAction(const QString & name, std::function f) { + toggleActions[name] = f; +} + +void HifiMenu::setTriggerAction(const QString & name, std::function f) { + triggerActions[name] = f; +} diff --git a/libraries/ui/src/MenuConstants.h b/libraries/ui/src/MenuConstants.h new file mode 100644 index 0000000000..e2d50b10a6 --- /dev/null +++ b/libraries/ui/src/MenuConstants.h @@ -0,0 +1,503 @@ +// +// MenuConstants.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_MenuContants_h +#define hifi_MenuConstants_h + +#include +#include +#include +#include "OffscreenQmlDialog.h" + +namespace MenuOption { + const QString AboutApp = "About Interface"; + const QString AddRemoveFriends = "Add/Remove Friends..."; + const QString AddressBar = "Show Address Bar"; + const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; + const QString AlternateIK = "Alternate IK"; + const QString AmbientOcclusion = "Ambient Occlusion"; + const QString Animations = "Animations..."; + const QString Atmosphere = "Atmosphere"; + const QString Attachments = "Attachments..."; + const QString AudioNoiseReduction = "Audio Noise Reduction"; + const QString AudioScope = "Show Scope"; + const QString AudioScopeFiftyFrames = "Fifty"; + const QString AudioScopeFiveFrames = "Five"; + const QString AudioScopeFrames = "Display Frames"; + const QString AudioScopePause = "Pause Scope"; + const QString AudioScopeTwentyFrames = "Twenty"; + const QString AudioStats = "Audio Stats"; + const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; + const QString BandwidthDetails = "Bandwidth Details"; + const QString BlueSpeechSphere = "Blue Sphere While Speaking"; + const QString BookmarkLocation = "Bookmark Location"; + const QString Bookmarks = "Bookmarks"; + const QString CascadedShadows = "Cascaded"; + const QString CachesSize = "RAM Caches Size"; + const QString Chat = "Chat..."; + const QString Collisions = "Collisions"; + const QString Console = "Console..."; + const QString ControlWithSpeech = "Control With Speech"; + const QString CopyAddress = "Copy Address to Clipboard"; + const QString CopyPath = "Copy Path to Clipboard"; + const QString DDEFaceRegression = "DDE Face Regression"; + const QString DDEFiltering = "DDE Filtering"; + const QString DecreaseAvatarSize = "Decrease Avatar Size"; + const QString DeleteBookmark = "Delete Bookmark..."; + const QString DisableActivityLogger = "Disable Activity Logger"; + const QString DisableLightEntities = "Disable Light Entities"; + const QString DisableNackPackets = "Disable NACK Packets"; + const QString DiskCacheEditor = "Disk Cache Editor"; + const QString DisplayHands = "Show Hand Info"; + const QString DisplayHandTargets = "Show Hand Targets"; + const QString DisplayModelBounds = "Display Model Bounds"; + const QString DisplayModelTriangles = "Display Model Triangles"; + const QString DisplayModelElementChildProxies = "Display Model Element Children"; + const QString DisplayModelElementProxy = "Display Model Element Bounds"; + const QString DisplayDebugTimingDetails = "Display Timing Details"; + const QString DontDoPrecisionPicking = "Don't Do Precision Picking"; + const QString DontFadeOnOctreeServerChanges = "Don't Fade In/Out on Octree Server Changes"; + const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; + const QString EchoLocalAudio = "Echo Local Audio"; + const QString EchoServerAudio = "Echo Server Audio"; + const QString EditEntitiesHelp = "Edit Entities Help..."; + const QString Enable3DTVMode = "Enable 3DTV Mode"; + const QString EnableCharacterController = "Enable avatar collisions"; + const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; + const QString EnableVRMode = "Enable VR Mode"; + const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; + const QString ExpandMyAvatarTiming = "Expand /myAvatar"; + const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; + const QString ExpandPaintGLTiming = "Expand /paintGL"; + const QString ExpandUpdateTiming = "Expand /update"; + const QString Faceshift = "Faceshift"; + const QString FilterSixense = "Smooth Sixense Movement"; + const QString FirstPerson = "First Person"; + const QString FrameTimer = "Show Timer"; + const QString Fullscreen = "Fullscreen"; + const QString FullscreenMirror = "Fullscreen Mirror"; + const QString GlowWhenSpeaking = "Glow When Speaking"; + const QString NamesAboveHeads = "Names Above Heads"; + const QString GoToUser = "Go To User"; + const QString HMDTools = "HMD Tools"; + const QString IncreaseAvatarSize = "Increase Avatar Size"; + const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; + const QString LeapMotionOnHMD = "Leap Motion on HMD"; + const QString LoadScript = "Open and Run Script File..."; + const QString LoadScriptURL = "Open and Run Script from URL..."; + const QString LoadRSSDKFile = "Load .rssdk file"; + const QString LodTools = "LOD Tools"; + const QString Login = "Login"; + const QString Log = "Log"; + const QString LowVelocityFilter = "Low Velocity Filter"; + const QString Mirror = "Mirror"; + const QString MuteAudio = "Mute Microphone"; + const QString MuteEnvironment = "Mute Environment"; + const QString NoFaceTracking = "None"; + const QString OctreeStats = "Entity Statistics"; + const QString OffAxisProjection = "Off-Axis Projection"; + const QString OnlyDisplayTopTen = "Only Display Top Ten"; + const QString PackageModel = "Package Model..."; + const QString Pair = "Pair"; + const QString PipelineWarnings = "Log Render Pipeline Warnings"; + const QString Preferences = "Preferences..."; + const QString Quit = "Quit"; + const QString ReloadAllScripts = "Reload All Scripts"; + const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; + const QString RenderFocusIndicator = "Show Eye Focus"; + const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; + const QString RenderLookAtVectors = "Show Look-at Vectors"; + const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; + const QString RenderTargetFramerate = "Framerate"; + const QString RenderTargetFramerateUnlimited = "Unlimited"; + const QString RenderTargetFramerate60 = "60"; + const QString RenderTargetFramerate50 = "50"; + const QString RenderTargetFramerate40 = "40"; + const QString RenderTargetFramerate30 = "30"; + const QString RenderTargetFramerateVSyncOn = "V-Sync On"; + const QString RenderResolution = "Scale Resolution"; + const QString RenderResolutionOne = "1"; + const QString RenderResolutionTwoThird = "2/3"; + const QString RenderResolutionHalf = "1/2"; + const QString RenderResolutionThird = "1/3"; + const QString RenderResolutionQuarter = "1/4"; + const QString RenderAmbientLight = "Ambient Light"; + const QString RenderAmbientLightGlobal = "Global"; + const QString RenderAmbientLight0 = "OLD_TOWN_SQUARE"; + const QString RenderAmbientLight1 = "GRACE_CATHEDRAL"; + const QString RenderAmbientLight2 = "EUCALYPTUS_GROVE"; + const QString RenderAmbientLight3 = "ST_PETERS_BASILICA"; + const QString RenderAmbientLight4 = "UFFIZI_GALLERY"; + const QString RenderAmbientLight5 = "GALILEOS_TOMB"; + const QString RenderAmbientLight6 = "VINE_STREET_KITCHEN"; + const QString RenderAmbientLight7 = "BREEZEWAY"; + const QString RenderAmbientLight8 = "CAMPUS_SUNSET"; + const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET"; + const QString ResetAvatarSize = "Reset Avatar Size"; + const QString ResetDDETracking = "Reset DDE Tracking"; + const QString ResetSensors = "Reset Sensors"; + const QString RunningScripts = "Running Scripts"; + const QString RunTimingTests = "Run Timing Tests"; + const QString ScriptEditor = "Script Editor..."; + const QString ScriptedMotorControl = "Enable Scripted Motor Control"; + const QString ShowBordersEntityNodes = "Show Entity Nodes"; + const QString ShowIKConstraints = "Show IK Constraints"; + const QString SimpleShadows = "Simple"; + const QString SixenseEnabled = "Enable Hydra Support"; + const QString SixenseMouseInput = "Enable Sixense Mouse Input"; + const QString SixenseLasers = "Enable Sixense UI Lasers"; + 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"; + const QString ToolWindow = "Tool Window"; + const QString TransmitterDrive = "Transmitter Drive"; + const QString TurnWithHead = "Turn using Head"; + const QString UseAudioForMouth = "Use Audio for Mouth"; + const QString VisibleToEveryone = "Everyone"; + const QString VisibleToFriends = "Friends"; + const QString VisibleToNoOne = "No one"; + const QString Wireframe = "Wireframe"; +} + + +class HifiMenu : public QQuickItem +{ + Q_OBJECT + QML_DIALOG_DECL + + Q_PROPERTY(QString aboutApp READ aboutApp CONSTANT) + Q_PROPERTY(QString addRemoveFriends READ addRemoveFriends CONSTANT) + Q_PROPERTY(QString addressBar READ addressBar CONSTANT) + Q_PROPERTY(QString alignForearmsWithWrists READ alignForearmsWithWrists CONSTANT) + Q_PROPERTY(QString alternateIK READ alternateIK CONSTANT) + Q_PROPERTY(QString ambientOcclusion READ ambientOcclusion CONSTANT) + Q_PROPERTY(QString animations READ animations CONSTANT) + Q_PROPERTY(QString atmosphere READ atmosphere CONSTANT) + Q_PROPERTY(QString attachments READ attachments CONSTANT) + Q_PROPERTY(QString audioNoiseReduction READ audioNoiseReduction CONSTANT) + Q_PROPERTY(QString audioScope READ audioScope CONSTANT) + Q_PROPERTY(QString audioScopeFiftyFrames READ audioScopeFiftyFrames CONSTANT) + Q_PROPERTY(QString audioScopeFiveFrames READ audioScopeFiveFrames CONSTANT) + Q_PROPERTY(QString audioScopeFrames READ audioScopeFrames CONSTANT) + Q_PROPERTY(QString audioScopePause READ audioScopePause CONSTANT) + Q_PROPERTY(QString audioScopeTwentyFrames READ audioScopeTwentyFrames CONSTANT) + Q_PROPERTY(QString audioStats READ audioStats CONSTANT) + Q_PROPERTY(QString audioStatsShowInjectedStreams READ audioStatsShowInjectedStreams CONSTANT) + Q_PROPERTY(QString bandwidthDetails READ bandwidthDetails CONSTANT) + Q_PROPERTY(QString blueSpeechSphere READ blueSpeechSphere CONSTANT) + Q_PROPERTY(QString bookmarkLocation READ bookmarkLocation CONSTANT) + Q_PROPERTY(QString bookmarks READ bookmarks CONSTANT) + Q_PROPERTY(QString cascadedShadows READ cascadedShadows CONSTANT) + Q_PROPERTY(QString cachesSize READ cachesSize CONSTANT) + Q_PROPERTY(QString chat READ chat CONSTANT) + Q_PROPERTY(QString collisions READ collisions CONSTANT) + Q_PROPERTY(QString console READ console CONSTANT) + Q_PROPERTY(QString controlWithSpeech READ controlWithSpeech CONSTANT) + Q_PROPERTY(QString copyAddress READ copyAddress CONSTANT) + Q_PROPERTY(QString copyPath READ copyPath CONSTANT) + Q_PROPERTY(QString ddeFaceRegression READ ddeFaceRegression CONSTANT) + Q_PROPERTY(QString ddeFiltering READ ddeFiltering CONSTANT) + Q_PROPERTY(QString decreaseAvatarSize READ decreaseAvatarSize CONSTANT) + Q_PROPERTY(QString deleteBookmark READ deleteBookmark CONSTANT) + Q_PROPERTY(QString disableActivityLogger READ disableActivityLogger CONSTANT) + Q_PROPERTY(QString disableLightEntities READ disableLightEntities CONSTANT) + Q_PROPERTY(QString disableNackPackets READ disableNackPackets CONSTANT) + Q_PROPERTY(QString diskCacheEditor READ diskCacheEditor CONSTANT) + Q_PROPERTY(QString displayHands READ displayHands CONSTANT) + Q_PROPERTY(QString displayHandTargets READ displayHandTargets CONSTANT) + Q_PROPERTY(QString displayModelBounds READ displayModelBounds CONSTANT) + Q_PROPERTY(QString displayModelTriangles READ displayModelTriangles CONSTANT) + Q_PROPERTY(QString displayModelElementChildProxies READ displayModelElementChildProxies CONSTANT) + Q_PROPERTY(QString displayModelElementProxy READ displayModelElementProxy CONSTANT) + Q_PROPERTY(QString displayDebugTimingDetails READ displayDebugTimingDetails CONSTANT) + Q_PROPERTY(QString dontDoPrecisionPicking READ dontDoPrecisionPicking CONSTANT) + Q_PROPERTY(QString dontFadeOnOctreeServerChanges READ dontFadeOnOctreeServerChanges CONSTANT) + Q_PROPERTY(QString dontRenderEntitiesAsScene READ dontRenderEntitiesAsScene CONSTANT) + Q_PROPERTY(QString echoLocalAudio READ echoLocalAudio CONSTANT) + Q_PROPERTY(QString echoServerAudio READ echoServerAudio CONSTANT) + Q_PROPERTY(QString editEntitiesHelp READ editEntitiesHelp CONSTANT) + Q_PROPERTY(QString enable3DTVMode READ enable3DTVMode CONSTANT) + Q_PROPERTY(QString enableCharacterController READ enableCharacterController CONSTANT) + Q_PROPERTY(QString enableGlowEffect READ enableGlowEffect CONSTANT) + Q_PROPERTY(QString enableVRMode READ enableVRMode CONSTANT) + Q_PROPERTY(QString expandMyAvatarSimulateTiming READ expandMyAvatarSimulateTiming CONSTANT) + Q_PROPERTY(QString expandMyAvatarTiming READ expandMyAvatarTiming CONSTANT) + Q_PROPERTY(QString expandOtherAvatarTiming READ expandOtherAvatarTiming CONSTANT) + Q_PROPERTY(QString expandPaintGLTiming READ expandPaintGLTiming CONSTANT) + Q_PROPERTY(QString expandUpdateTiming READ expandUpdateTiming CONSTANT) + Q_PROPERTY(QString faceshift READ faceshift CONSTANT) + Q_PROPERTY(QString filterSixense READ filterSixense CONSTANT) + Q_PROPERTY(QString firstPerson READ firstPerson CONSTANT) + Q_PROPERTY(QString frameTimer READ frameTimer CONSTANT) + Q_PROPERTY(QString fullscreen READ fullscreen CONSTANT) + Q_PROPERTY(QString fullscreenMirror READ fullscreenMirror CONSTANT) + Q_PROPERTY(QString glowWhenSpeaking READ glowWhenSpeaking CONSTANT) + Q_PROPERTY(QString namesAboveHeads READ namesAboveHeads CONSTANT) + Q_PROPERTY(QString goToUser READ goToUser CONSTANT) + Q_PROPERTY(QString hmdTools READ hmdTools CONSTANT) + Q_PROPERTY(QString increaseAvatarSize READ increaseAvatarSize CONSTANT) + Q_PROPERTY(QString keyboardMotorControl READ keyboardMotorControl CONSTANT) + Q_PROPERTY(QString leapMotionOnHMD READ leapMotionOnHMD CONSTANT) + Q_PROPERTY(QString loadScript READ loadScript CONSTANT) + Q_PROPERTY(QString loadScriptURL READ loadScriptURL CONSTANT) + Q_PROPERTY(QString loadRSSDKFile READ loadRSSDKFile CONSTANT) + Q_PROPERTY(QString lodTools READ lodTools CONSTANT) + Q_PROPERTY(QString login READ login CONSTANT) + Q_PROPERTY(QString log READ log CONSTANT) + Q_PROPERTY(QString lowVelocityFilter READ lowVelocityFilter CONSTANT) + Q_PROPERTY(QString mirror READ mirror CONSTANT) + Q_PROPERTY(QString muteAudio READ muteAudio CONSTANT) + Q_PROPERTY(QString muteEnvironment READ muteEnvironment CONSTANT) + Q_PROPERTY(QString noFaceTracking READ noFaceTracking CONSTANT) + Q_PROPERTY(QString octreeStats READ octreeStats CONSTANT) + Q_PROPERTY(QString offAxisProjection READ offAxisProjection CONSTANT) + Q_PROPERTY(QString onlyDisplayTopTen READ onlyDisplayTopTen CONSTANT) + Q_PROPERTY(QString packageModel READ packageModel CONSTANT) + Q_PROPERTY(QString pair READ pair CONSTANT) + Q_PROPERTY(QString pipelineWarnings READ pipelineWarnings CONSTANT) + Q_PROPERTY(QString preferences READ preferences CONSTANT) + Q_PROPERTY(QString quit READ quit CONSTANT) + Q_PROPERTY(QString reloadAllScripts READ reloadAllScripts CONSTANT) + Q_PROPERTY(QString renderBoundingCollisionShapes READ renderBoundingCollisionShapes CONSTANT) + Q_PROPERTY(QString renderFocusIndicator READ renderFocusIndicator CONSTANT) + Q_PROPERTY(QString renderHeadCollisionShapes READ renderHeadCollisionShapes CONSTANT) + Q_PROPERTY(QString renderLookAtVectors READ renderLookAtVectors CONSTANT) + Q_PROPERTY(QString renderSkeletonCollisionShapes READ renderSkeletonCollisionShapes CONSTANT) + Q_PROPERTY(QString renderTargetFramerate READ renderTargetFramerate CONSTANT) + Q_PROPERTY(QString renderTargetFramerateUnlimited READ renderTargetFramerateUnlimited CONSTANT) + Q_PROPERTY(QString renderTargetFramerate60 READ renderTargetFramerate60 CONSTANT) + Q_PROPERTY(QString renderTargetFramerate50 READ renderTargetFramerate50 CONSTANT) + Q_PROPERTY(QString renderTargetFramerate40 READ renderTargetFramerate40 CONSTANT) + Q_PROPERTY(QString renderTargetFramerate30 READ renderTargetFramerate30 CONSTANT) + Q_PROPERTY(QString renderTargetFramerateVSyncOn READ renderTargetFramerateVSyncOn CONSTANT) + Q_PROPERTY(QString renderResolution READ renderResolution CONSTANT) + Q_PROPERTY(QString renderResolutionOne READ renderResolutionOne CONSTANT) + Q_PROPERTY(QString renderResolutionTwoThird READ renderResolutionTwoThird CONSTANT) + Q_PROPERTY(QString renderResolutionHalf READ renderResolutionHalf CONSTANT) + Q_PROPERTY(QString renderResolutionThird READ renderResolutionThird CONSTANT) + Q_PROPERTY(QString renderResolutionQuarter READ renderResolutionQuarter CONSTANT) + Q_PROPERTY(QString renderAmbientLight READ renderAmbientLight CONSTANT) + Q_PROPERTY(QString renderAmbientLightGlobal READ renderAmbientLightGlobal CONSTANT) + Q_PROPERTY(QString renderAmbientLight0 READ renderAmbientLight0 CONSTANT) + Q_PROPERTY(QString renderAmbientLight1 READ renderAmbientLight1 CONSTANT) + Q_PROPERTY(QString renderAmbientLight2 READ renderAmbientLight2 CONSTANT) + Q_PROPERTY(QString renderAmbientLight3 READ renderAmbientLight3 CONSTANT) + Q_PROPERTY(QString renderAmbientLight4 READ renderAmbientLight4 CONSTANT) + Q_PROPERTY(QString renderAmbientLight5 READ renderAmbientLight5 CONSTANT) + Q_PROPERTY(QString renderAmbientLight6 READ renderAmbientLight6 CONSTANT) + Q_PROPERTY(QString renderAmbientLight7 READ renderAmbientLight7 CONSTANT) + Q_PROPERTY(QString renderAmbientLight8 READ renderAmbientLight8 CONSTANT) + Q_PROPERTY(QString renderAmbientLight9 READ renderAmbientLight9 CONSTANT) + Q_PROPERTY(QString resetAvatarSize READ resetAvatarSize CONSTANT) + Q_PROPERTY(QString resetDDETracking READ resetDDETracking CONSTANT) + Q_PROPERTY(QString resetSensors READ resetSensors CONSTANT) + Q_PROPERTY(QString runningScripts READ runningScripts CONSTANT) + Q_PROPERTY(QString runTimingTests READ runTimingTests CONSTANT) + Q_PROPERTY(QString scriptEditor READ scriptEditor CONSTANT) + Q_PROPERTY(QString scriptedMotorControl READ scriptedMotorControl CONSTANT) + Q_PROPERTY(QString showBordersEntityNodes READ showBordersEntityNodes CONSTANT) + Q_PROPERTY(QString showIKConstraints READ showIKConstraints CONSTANT) + Q_PROPERTY(QString simpleShadows READ simpleShadows CONSTANT) + Q_PROPERTY(QString sixenseEnabled READ sixenseEnabled CONSTANT) + Q_PROPERTY(QString sixenseMouseInput READ sixenseMouseInput CONSTANT) + Q_PROPERTY(QString sixenseLasers READ sixenseLasers CONSTANT) + Q_PROPERTY(QString shiftHipsForIdleAnimations READ shiftHipsForIdleAnimations CONSTANT) + Q_PROPERTY(QString stars READ stars CONSTANT) + Q_PROPERTY(QString stats READ stats CONSTANT) + Q_PROPERTY(QString stereoAudio READ stereoAudio CONSTANT) + Q_PROPERTY(QString stopAllScripts READ stopAllScripts CONSTANT) + Q_PROPERTY(QString suppressShortTimings READ suppressShortTimings CONSTANT) + Q_PROPERTY(QString testPing READ testPing CONSTANT) + Q_PROPERTY(QString toolWindow READ toolWindow CONSTANT) + Q_PROPERTY(QString transmitterDrive READ transmitterDrive CONSTANT) + Q_PROPERTY(QString turnWithHead READ turnWithHead CONSTANT) + Q_PROPERTY(QString useAudioForMouth READ useAudioForMouth CONSTANT) + Q_PROPERTY(QString visibleToEveryone READ visibleToEveryone CONSTANT) + Q_PROPERTY(QString visibleToFriends READ visibleToFriends CONSTANT) + Q_PROPERTY(QString visibleToNoOne READ visibleToNoOne CONSTANT) + Q_PROPERTY(QString wireframe READ wireframe CONSTANT) + +public: + + const QString & aboutApp() { return MenuOption::AboutApp; } + const QString & addRemoveFriends() { return MenuOption::AddRemoveFriends; } + const QString & addressBar() { return MenuOption::AddressBar; } + const QString & alignForearmsWithWrists() { return MenuOption::AlignForearmsWithWrists; } + const QString & alternateIK() { return MenuOption::AlternateIK; } + const QString & ambientOcclusion() { return MenuOption::AmbientOcclusion; } + const QString & animations() { return MenuOption::Animations; } + const QString & atmosphere() { return MenuOption::Atmosphere; } + const QString & attachments() { return MenuOption::Attachments; } + const QString & audioNoiseReduction() { return MenuOption::AudioNoiseReduction; } + const QString & audioScope() { return MenuOption::AudioScope; } + const QString & audioScopeFiftyFrames() { return MenuOption::AudioScopeFiftyFrames; } + const QString & audioScopeFiveFrames() { return MenuOption::AudioScopeFiveFrames; } + const QString & audioScopeFrames() { return MenuOption::AudioScopeFrames; } + const QString & audioScopePause() { return MenuOption::AudioScopePause; } + const QString & audioScopeTwentyFrames() { return MenuOption::AudioScopeTwentyFrames; } + const QString & audioStats() { return MenuOption::AudioStats; } + const QString & audioStatsShowInjectedStreams() { return MenuOption::AudioStatsShowInjectedStreams; } + const QString & bandwidthDetails() { return MenuOption::BandwidthDetails; } + const QString & blueSpeechSphere() { return MenuOption::BlueSpeechSphere; } + const QString & bookmarkLocation() { return MenuOption::BookmarkLocation; } + const QString & bookmarks() { return MenuOption::Bookmarks; } + const QString & cascadedShadows() { return MenuOption::CascadedShadows; } + const QString & cachesSize() { return MenuOption::CachesSize; } + const QString & chat() { return MenuOption::Chat; } + const QString & collisions() { return MenuOption::Collisions; } + const QString & console() { return MenuOption::Console; } + const QString & controlWithSpeech() { return MenuOption::ControlWithSpeech; } + const QString & copyAddress() { return MenuOption::CopyAddress; } + const QString & copyPath() { return MenuOption::CopyPath; } + const QString & ddeFaceRegression() { return MenuOption::DDEFaceRegression; } + const QString & ddeFiltering() { return MenuOption::DDEFiltering; } + const QString & decreaseAvatarSize() { return MenuOption::DecreaseAvatarSize; } + const QString & deleteBookmark() { return MenuOption::DeleteBookmark; } + const QString & disableActivityLogger() { return MenuOption::DisableActivityLogger; } + const QString & disableLightEntities() { return MenuOption::DisableLightEntities; } + const QString & disableNackPackets() { return MenuOption::DisableNackPackets; } + const QString & diskCacheEditor() { return MenuOption::DiskCacheEditor; } + const QString & displayHands() { return MenuOption::DisplayHands; } + const QString & displayHandTargets() { return MenuOption::DisplayHandTargets; } + const QString & displayModelBounds() { return MenuOption::DisplayModelBounds; } + const QString & displayModelTriangles() { return MenuOption::DisplayModelTriangles; } + const QString & displayModelElementChildProxies() { return MenuOption::DisplayModelElementChildProxies; } + const QString & displayModelElementProxy() { return MenuOption::DisplayModelElementProxy; } + const QString & displayDebugTimingDetails() { return MenuOption::DisplayDebugTimingDetails; } + const QString & dontDoPrecisionPicking() { return MenuOption::DontDoPrecisionPicking; } + const QString & dontFadeOnOctreeServerChanges() { return MenuOption::DontFadeOnOctreeServerChanges; } + const QString & dontRenderEntitiesAsScene() { return MenuOption::DontRenderEntitiesAsScene; } + const QString & echoLocalAudio() { return MenuOption::EchoLocalAudio; } + const QString & echoServerAudio() { return MenuOption::EchoServerAudio; } + const QString & editEntitiesHelp() { return MenuOption::EditEntitiesHelp; } + const QString & enable3DTVMode() { return MenuOption::Enable3DTVMode; } + const QString & enableCharacterController() { return MenuOption::EnableCharacterController; } + const QString & enableGlowEffect() { return MenuOption::EnableGlowEffect; } + const QString & enableVRMode() { return MenuOption::EnableVRMode; } + const QString & expandMyAvatarSimulateTiming() { return MenuOption::ExpandMyAvatarSimulateTiming; } + const QString & expandMyAvatarTiming() { return MenuOption::ExpandMyAvatarTiming; } + const QString & expandOtherAvatarTiming() { return MenuOption::ExpandOtherAvatarTiming; } + const QString & expandPaintGLTiming() { return MenuOption::ExpandPaintGLTiming; } + const QString & expandUpdateTiming() { return MenuOption::ExpandUpdateTiming; } + const QString & faceshift() { return MenuOption::Faceshift; } + const QString & filterSixense() { return MenuOption::FilterSixense; } + const QString & firstPerson() { return MenuOption::FirstPerson; } + const QString & frameTimer() { return MenuOption::FrameTimer; } + const QString & fullscreen() { return MenuOption::Fullscreen; } + const QString & fullscreenMirror() { return MenuOption::FullscreenMirror; } + const QString & glowWhenSpeaking() { return MenuOption::GlowWhenSpeaking; } + const QString & namesAboveHeads() { return MenuOption::NamesAboveHeads; } + const QString & goToUser() { return MenuOption::GoToUser; } + const QString & hmdTools() { return MenuOption::HMDTools; } + const QString & increaseAvatarSize() { return MenuOption::IncreaseAvatarSize; } + const QString & keyboardMotorControl() { return MenuOption::KeyboardMotorControl; } + const QString & leapMotionOnHMD() { return MenuOption::LeapMotionOnHMD; } + const QString & loadScript() { return MenuOption::LoadScript; } + const QString & loadScriptURL() { return MenuOption::LoadScriptURL; } + const QString & loadRSSDKFile() { return MenuOption::LoadRSSDKFile; } + const QString & lodTools() { return MenuOption::LodTools; } + const QString & login() { return MenuOption::Login; } + const QString & log() { return MenuOption::Log; } + const QString & lowVelocityFilter() { return MenuOption::LowVelocityFilter; } + const QString & mirror() { return MenuOption::Mirror; } + const QString & muteAudio() { return MenuOption::MuteAudio; } + const QString & muteEnvironment() { return MenuOption::MuteEnvironment; } + const QString & noFaceTracking() { return MenuOption::NoFaceTracking; } + const QString & octreeStats() { return MenuOption::OctreeStats; } + const QString & offAxisProjection() { return MenuOption::OffAxisProjection; } + const QString & onlyDisplayTopTen() { return MenuOption::OnlyDisplayTopTen; } + const QString & packageModel() { return MenuOption::PackageModel; } + const QString & pair() { return MenuOption::Pair; } + const QString & pipelineWarnings() { return MenuOption::PipelineWarnings; } + const QString & preferences() { return MenuOption::Preferences; } + const QString & quit() { return MenuOption::Quit; } + const QString & reloadAllScripts() { return MenuOption::ReloadAllScripts; } + const QString & renderBoundingCollisionShapes() { return MenuOption::RenderBoundingCollisionShapes; } + const QString & renderFocusIndicator() { return MenuOption::RenderFocusIndicator; } + const QString & renderHeadCollisionShapes() { return MenuOption::RenderHeadCollisionShapes; } + const QString & renderLookAtVectors() { return MenuOption::RenderLookAtVectors; } + const QString & renderSkeletonCollisionShapes() { return MenuOption::RenderSkeletonCollisionShapes; } + const QString & renderTargetFramerate() { return MenuOption::RenderTargetFramerate; } + const QString & renderTargetFramerateUnlimited() { return MenuOption::RenderTargetFramerateUnlimited; } + const QString & renderTargetFramerate60() { return MenuOption::RenderTargetFramerate60; } + const QString & renderTargetFramerate50() { return MenuOption::RenderTargetFramerate50; } + const QString & renderTargetFramerate40() { return MenuOption::RenderTargetFramerate40; } + const QString & renderTargetFramerate30() { return MenuOption::RenderTargetFramerate30; } + const QString & renderTargetFramerateVSyncOn() { return MenuOption::RenderTargetFramerateVSyncOn; } + const QString & renderResolution() { return MenuOption::RenderResolution; } + const QString & renderResolutionOne() { return MenuOption::RenderResolutionOne; } + const QString & renderResolutionTwoThird() { return MenuOption::RenderResolutionTwoThird; } + const QString & renderResolutionHalf() { return MenuOption::RenderResolutionHalf; } + const QString & renderResolutionThird() { return MenuOption::RenderResolutionThird; } + const QString & renderResolutionQuarter() { return MenuOption::RenderResolutionQuarter; } + const QString & renderAmbientLight() { return MenuOption::RenderAmbientLight; } + const QString & renderAmbientLightGlobal() { return MenuOption::RenderAmbientLightGlobal; } + const QString & renderAmbientLight0() { return MenuOption::RenderAmbientLight0; } + const QString & renderAmbientLight1() { return MenuOption::RenderAmbientLight1; } + const QString & renderAmbientLight2() { return MenuOption::RenderAmbientLight2; } + const QString & renderAmbientLight3() { return MenuOption::RenderAmbientLight3; } + const QString & renderAmbientLight4() { return MenuOption::RenderAmbientLight4; } + const QString & renderAmbientLight5() { return MenuOption::RenderAmbientLight5; } + const QString & renderAmbientLight6() { return MenuOption::RenderAmbientLight6; } + const QString & renderAmbientLight7() { return MenuOption::RenderAmbientLight7; } + const QString & renderAmbientLight8() { return MenuOption::RenderAmbientLight8; } + const QString & renderAmbientLight9() { return MenuOption::RenderAmbientLight9; } + const QString & resetAvatarSize() { return MenuOption::ResetAvatarSize; } + const QString & resetDDETracking() { return MenuOption::ResetDDETracking; } + const QString & resetSensors() { return MenuOption::ResetSensors; } + const QString & runningScripts() { return MenuOption::RunningScripts; } + const QString & runTimingTests() { return MenuOption::RunTimingTests; } + const QString & scriptEditor() { return MenuOption::ScriptEditor; } + const QString & scriptedMotorControl() { return MenuOption::ScriptedMotorControl; } + const QString & showBordersEntityNodes() { return MenuOption::ShowBordersEntityNodes; } + const QString & showIKConstraints() { return MenuOption::ShowIKConstraints; } + const QString & simpleShadows() { return MenuOption::SimpleShadows; } + const QString & sixenseEnabled() { return MenuOption::SixenseEnabled; } + const QString & sixenseMouseInput() { return MenuOption::SixenseMouseInput; } + const QString & sixenseLasers() { return MenuOption::SixenseLasers; } + const QString & shiftHipsForIdleAnimations() { return MenuOption::ShiftHipsForIdleAnimations; } + const QString & stars() { return MenuOption::Stars; } + const QString & stats() { return MenuOption::Stats; } + const QString & stereoAudio() { return MenuOption::StereoAudio; } + const QString & stopAllScripts() { return MenuOption::StopAllScripts; } + const QString & suppressShortTimings() { return MenuOption::SuppressShortTimings; } + const QString & testPing() { return MenuOption::TestPing; } + const QString & toolWindow() { return MenuOption::ToolWindow; } + const QString & transmitterDrive() { return MenuOption::TransmitterDrive; } + const QString & turnWithHead() { return MenuOption::TurnWithHead; } + const QString & useAudioForMouth() { return MenuOption::UseAudioForMouth; } + const QString & visibleToEveryone() { return MenuOption::VisibleToEveryone; } + const QString & visibleToFriends() { return MenuOption::VisibleToFriends; } + const QString & visibleToNoOne() { return MenuOption::VisibleToNoOne; } + const QString & wireframe() { return MenuOption::Wireframe; } + +public slots: + void onTriggeredByName(const QString & name); + void onToggledByName(const QString & name); + +public: + HifiMenu(QQuickItem * parent = nullptr); + static void setToggleAction(const QString & name, std::function f); + static void setTriggerAction(const QString & name, std::function f); +private: + static QHash> triggerActions; + static QHash> toggleActions; +}; + +#endif // hifi_MenuConstants_h + + + + diff --git a/libraries/render-utils/src/MessageDialog.cpp b/libraries/ui/src/MessageDialog.cpp similarity index 100% rename from libraries/render-utils/src/MessageDialog.cpp rename to libraries/ui/src/MessageDialog.cpp diff --git a/libraries/render-utils/src/MessageDialog.h b/libraries/ui/src/MessageDialog.h similarity index 100% rename from libraries/render-utils/src/MessageDialog.h rename to libraries/ui/src/MessageDialog.h diff --git a/libraries/render-utils/src/OffscreenQmlDialog.cpp b/libraries/ui/src/OffscreenQmlDialog.cpp similarity index 100% rename from libraries/render-utils/src/OffscreenQmlDialog.cpp rename to libraries/ui/src/OffscreenQmlDialog.cpp diff --git a/libraries/render-utils/src/OffscreenQmlDialog.h b/libraries/ui/src/OffscreenQmlDialog.h similarity index 100% rename from libraries/render-utils/src/OffscreenQmlDialog.h rename to libraries/ui/src/OffscreenQmlDialog.h diff --git a/libraries/render-utils/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp similarity index 99% rename from libraries/render-utils/src/OffscreenUi.cpp rename to libraries/ui/src/OffscreenUi.cpp index d25d78300a..b14951f054 100644 --- a/libraries/render-utils/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -118,15 +118,17 @@ void OffscreenUi::resize(const QSize& newSize) { qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio; _fboCache.setSize(newSize * pixelRatio); + if (_quickWindow) { + _quickWindow->setGeometry(QRect(QPoint(), newSize)); + } + + _quickWindow->contentItem()->setSize(newSize); + // Update our members if (_rootItem) { _rootItem->setSize(newSize); } - if (_quickWindow) { - _quickWindow->setGeometry(QRect(QPoint(), newSize)); - } - doneCurrent(); } @@ -190,6 +192,7 @@ void OffscreenUi::finishQmlLoad(std::function f QQuickItem* newItem = qobject_cast(newObject); if (!newItem) { qWarning("run: Not a QQuickItem"); + return; delete newObject; if (!_rootItem) { qFatal("Unable to find root QQuickItem"); @@ -206,6 +209,7 @@ void OffscreenUi::finishQmlLoad(std::function f _rootItem = newItem; _rootItem->setParentItem(_quickWindow->contentItem()); _rootItem->setSize(_quickWindow->renderTargetSize()); + _rootItem->forceActiveFocus(); } else { // Allow child windows to be destroyed from JS QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership); diff --git a/libraries/render-utils/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h similarity index 78% rename from libraries/render-utils/src/OffscreenUi.h rename to libraries/ui/src/OffscreenUi.h index b2805b2ded..64837baf2d 100644 --- a/libraries/render-utils/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -32,6 +32,39 @@ #include "FboCache.h" #include +#define HIFI_QML_DECL \ +private: \ + static const QString NAME; \ + static const QUrl QML; \ +public: \ + static void registerType(); \ + static void show(std::function f = [](QQmlContext*, QQuickItem*) {}); \ + static void toggle(std::function f = [](QQmlContext*, QQuickItem*) {}); \ + static void load(std::function f = [](QQmlContext*, QQuickItem*) {}); \ +private: + +#define HIFI_QML_DEF(x) \ + const QUrl x::QML = QUrl(#x ".qml"); \ + const QString x::NAME = #x; \ + \ + void x::registerType() { \ + qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \ + } \ + \ + void x::show(std::function f) { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->show(QML, NAME, f); \ + } \ + \ + void x::toggle(std::function f) { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->toggle(QML, NAME, f); \ + } \ + void x::load(std::function f) { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->load(QML, f); \ + } + class OffscreenUi : public OffscreenGlCanvas, public Dependency { Q_OBJECT diff --git a/tests/render-utils/src/main.cpp b/tests/render-utils/src/main.cpp index 28d542e51a..5e45bf23a2 100644 --- a/tests/render-utils/src/main.cpp +++ b/tests/render-utils/src/main.cpp @@ -10,7 +10,6 @@ #include "TextRenderer.h" #include "MatrixStack.h" -#include "OffscreenUi.h" #include #include @@ -27,7 +26,6 @@ #include #include #include -#include #include #include @@ -82,6 +80,7 @@ const QString& getQmlDir() { } return dir; } + // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow { Q_OBJECT @@ -90,24 +89,17 @@ class QTestWindow : public QWindow { QSize _size; TextRenderer* _textRenderer[4]; RateCounter fps; - int testQmlTexture{ 0 }; - //ProgramPtr _planeProgam; - //ShapeWrapperPtr _planeShape; protected: - void renderText(); - void renderQml(); private: void resizeWindow(const QSize& size) { _size = size; - DependencyManager::get()->resize(_size); } public: QTestWindow() { - DependencyManager::set(); setSurfaceType(QSurface::OpenGLSurface); QSurfaceFormat format; @@ -167,32 +159,10 @@ public: glClearColor(0.2f, 0.2f, 0.2f, 1); glDisable(GL_DEPTH_TEST); - auto offscreenUi = DependencyManager::get(); - offscreenUi->create(_context); - // FIXME, need to switch to a QWindow for mouse and keyboard input to work - offscreenUi->setProxyWindow(this); - // "#0e7077" + makeCurrent(); + setFramePosition(QPoint(-1000, 0)); resize(QSize(800, 600)); - - offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); - offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->addImportPath(getQmlDir()); - offscreenUi->addImportPath("."); - - connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { - offscreenUi->lockTexture(textureId); - assert(!glGetError()); - GLuint oldTexture = testQmlTexture; - testQmlTexture = textureId; - if (oldTexture) { - offscreenUi->releaseTexture(oldTexture); - } - }); - installEventFilter(offscreenUi.data()); - offscreenUi->resume(); - QWebEnginePage *page = new QWebEnginePage; - page->runJavaScript("'Java''' 'Script'", [](const QVariant &result) { qDebug() << result; }); } virtual ~QTestWindow() { @@ -208,41 +178,6 @@ protected: void resizeEvent(QResizeEvent* ev) override { resizeWindow(ev->size()); } - - - void keyPressEvent(QKeyEvent* event) { - switch (event->key()) { - case Qt::Key_L: - if (event->modifiers() & Qt::CTRL) { - auto offscreenUi = DependencyManager::get(); - offscreenUi->qmlEngine()->clearComponentCache(); - DependencyManager::get()->toggle(QString("TestDialog.qml"), "TestDialog"); - } - break; - case Qt::Key_K: - if (event->modifiers() & Qt::CTRL) { - DependencyManager::get()->toggle(QString("Browser.qml"), "Browser"); - } - break; - case Qt::Key_J: - if (event->modifiers() & Qt::CTRL) { - QObject * obj = DependencyManager::get()->findObject("WebView"); - qDebug() << obj; - } - break; - } - QWindow::keyPressEvent(event); - } - - void moveEvent(QMoveEvent* event) { - static qreal oldPixelRatio = 0.0; - if (devicePixelRatio() != oldPixelRatio) { - oldPixelRatio = devicePixelRatio(); - resizeWindow(size()); - } - - QWindow::moveEvent(event); - } }; #ifndef SERIF_FONT_FAMILY @@ -299,39 +234,16 @@ void QTestWindow::renderText() { } } -void QTestWindow::renderQml() { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - if (testQmlTexture > 0) { - glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, testQmlTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - glBegin(GL_QUADS); - { - glTexCoord2f(0, 0); - glVertex2f(-1, -1); - glTexCoord2f(0, 1); - glVertex2f(-1, 1); - glTexCoord2f(1, 1); - glVertex2f(1, 1); - glTexCoord2f(1, 0); - glVertex2f(1, -1); - } - glEnd(); -} - void QTestWindow::draw() { + if (!isVisible()) { + return; + } + makeCurrent(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio()); - //renderText(); - renderQml(); + renderText(); _context->swapBuffers(this); glFinish(); @@ -344,10 +256,8 @@ void QTestWindow::draw() { } int main(int argc, char** argv) { - QApplication app(argc, argv); - //QLoggingCategory::setFilterRules("qt.quick.mouse.debug = true"); + QGuiApplication app(argc, argv); QTestWindow window; - QTimer timer; timer.setInterval(1); app.connect(&timer, &QTimer::timeout, &app, [&] { diff --git a/tests/ui/CMakeLists.txt b/tests/ui/CMakeLists.txt new file mode 100644 index 0000000000..3ff8555fa2 --- /dev/null +++ b/tests/ui/CMakeLists.txt @@ -0,0 +1,15 @@ +set(TARGET_NAME ui-tests) + +setup_hifi_project(Widgets OpenGL Network Qml Quick Script) + +if (WIN32) + add_dependency_external_projects(glew) + find_package(GLEW REQUIRED) + target_include_directories(${TARGET_NAME} PRIVATE ${GLEW_INCLUDE_DIRS}) + target_link_libraries(${TARGET_NAME} ${GLEW_LIBRARIES} wsock32.lib opengl32.lib Winmm.lib) +endif() + +# link in the shared libraries +link_hifi_libraries(ui render-utils gpu shared) + +copy_dlls_beside_windows_executable() \ No newline at end of file diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp new file mode 100644 index 0000000000..00d0bc80fd --- /dev/null +++ b/tests/ui/src/main.cpp @@ -0,0 +1,302 @@ +// +// main.cpp +// tests/render-utils/src +// +// Copyright 2014 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 "OffscreenUi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "MessageDialog.h" +#include "MenuConstants.h" + +class RateCounter { + std::vector times; + QElapsedTimer timer; +public: + RateCounter() { + timer.start(); + } + + void reset() { + times.clear(); + } + + unsigned int count() const { + return times.size() - 1; + } + + float elapsed() const { + if (times.size() < 1) { + return 0.0f; + } + float elapsed = *times.rbegin() - *times.begin(); + return elapsed; + } + + void increment() { + times.push_back(timer.elapsed() / 1000.0f); + } + + float rate() const { + if (elapsed() == 0.0f) { + return NAN; + } + return (float) count() / elapsed(); + } +}; + + +const QString & getQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/qml/")) + "/"; + qDebug() << "Qml Path: " << dir; + } + return dir; +} + +// Create a simple OpenGL window that renders text in various ways +class QTestWindow : public QWindow, private QOpenGLFunctions { + Q_OBJECT + + QOpenGLContext * _context{ nullptr }; + QSize _size; + bool _altPressed{ false }; + RateCounter fps; + QTimer _timer; + int testQmlTexture{ 0 }; + +public: + QTestWindow() { + _timer.setInterval(1); + connect(&_timer, &QTimer::timeout, [=] { + draw(); + }); + + DependencyManager::set(); + setSurfaceType(QSurface::OpenGLSurface); + + QSurfaceFormat format; + format.setDepthBufferSize(16); + format.setStencilBufferSize(8); + format.setVersion(4, 1); + format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile); + format.setOption(QSurfaceFormat::DebugContext); + + setFormat(format); + + _context = new QOpenGLContext; + _context->setFormat(format); + if (!_context->create()) { + qFatal("Could not create OpenGL context"); + } + + show(); + makeCurrent(); + initializeOpenGLFunctions(); + + { + 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) { + qDebug() << debugMessage; + }); + // logger->startLogging(QOpenGLDebugLogger::SynchronousLogging); + } + + qDebug() << (const char*)this->glGetString(GL_VERSION); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glClearColor(0.2f, 0.2f, 0.2f, 1); + glDisable(GL_DEPTH_TEST); + + MessageDialog::registerType(); + HifiMenu::registerType(); + + auto offscreenUi = DependencyManager::get(); + offscreenUi->create(_context); + + makeCurrent(); + + offscreenUi->setProxyWindow(this); + setFramePosition(QPoint(-1000, 0)); + resize(QSize(800, 600)); + + offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); + offscreenUi->load(QUrl("TestRoot.qml")); + offscreenUi->addImportPath(getQmlDir()); + offscreenUi->addImportPath("."); + + connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { + offscreenUi->lockTexture(textureId); + assert(!glGetError()); + GLuint oldTexture = testQmlTexture; + testQmlTexture = textureId; + if (oldTexture) { + offscreenUi->releaseTexture(oldTexture); + } + }); + installEventFilter(offscreenUi.data()); + HifiMenu::setTriggerAction(MenuOption::Quit, [] { + QApplication::quit(); + }); + offscreenUi->resume(); + _timer.start(); + } + + virtual ~QTestWindow() { + DependencyManager::destroy(); + } + +private: + void draw() { + if (!isVisible()) { + return; + } + + makeCurrent(); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio()); + + renderQml(); + + _context->swapBuffers(this); + glFinish(); + + fps.increment(); + if (fps.elapsed() >= 2.0f) { + qDebug() << "FPS: " << fps.rate(); + fps.reset(); + } + } + + void makeCurrent() { + _context->makeCurrent(this); + } + + void renderQml(); + + void resizeWindow(const QSize & size) { + _size = size; + DependencyManager::get()->resize(_size); + } + + +protected: + void resizeEvent(QResizeEvent * ev) override { + resizeWindow(ev->size()); + } + + 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(); + //DependencyManager::get()->toggle(QString("TestDialog.qml"), "TestDialog"); + //DependencyManager::get()->toggle(QString("MenuTest.qml"), "MenuTest"); + //HifiMenu::toggle(); + } + break; + case Qt::Key_K: + if (event->modifiers() & Qt::CTRL) { + OffscreenUi::question("Message title", "Message contents", [](QMessageBox::Button b){ + qDebug() << b; + }); + } + break; + case Qt::Key_J: + if (event->modifiers() & Qt::CTRL) { + } + break; + } + QWindow::keyPressEvent(event); + } + + void keyReleaseEvent(QKeyEvent *event) { + if (_altPressed && Qt::Key_Alt == event->key()) { + HifiMenu::toggle(); + } + } + + void moveEvent(QMoveEvent *event) { + static qreal oldPixelRatio = 0.0; + if (devicePixelRatio() != oldPixelRatio) { + oldPixelRatio = devicePixelRatio(); + resizeWindow(size()); + } + QWindow::moveEvent(event); + } +}; + +void QTestWindow::renderQml() { + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + if (testQmlTexture > 0) { + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, testQmlTexture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + glBegin(GL_QUADS); + { + glTexCoord2f(0, 0); + glVertex2f(-1, -1); + glTexCoord2f(0, 1); + glVertex2f(-1, 1); + glTexCoord2f(1, 1); + glVertex2f(1, 1); + glTexCoord2f(1, 0); + glVertex2f(1, -1); + } + glEnd(); +} + + +const char * LOG_FILTER_RULES = R"V0G0N( +*.debug=false +qt.quick.dialogs.registration=true +qt.quick.mouse.debug = true +)V0G0N"; + +int main(int argc, char** argv) { + QGuiApplication app(argc, argv); + //QLoggingCategory::setFilterRules(LOG_FILTER_RULES); + QTestWindow window; + app.exec(); + return 0; +} + +#include "main.moc" From 9a8207610d1e10b106355dacd8816c71e6d6c436 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Apr 2015 19:56:47 -0700 Subject: [PATCH 06/33] Bad merge --- libraries/ui/src/OffscreenUi.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 64837baf2d..d04eb63bff 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -96,8 +96,8 @@ public: void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QQuickItem*) {}) { load(QUrl(qmlSourceFile), f); } - void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {})); - void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {})); + void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {}); + void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {}); void setBaseUrl(const QUrl& baseUrl); void addImportPath(const QString& path); QQmlContext* qmlContext(); @@ -119,7 +119,6 @@ public: static ButtonCallback NO_OP_CALLBACK; static void messageBox(const QString& title, const QString& text, - static void messageBox(const QString &title, const QString &text, ButtonCallback f, QMessageBox::Icon icon, QMessageBox::StandardButtons buttons); From bb1c73adc7e1730ad7568a5195f204d09f04c3f3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Apr 2015 00:24:24 -0700 Subject: [PATCH 07/33] Working on menus --- .../resources/qml/{Menu.qml => HifiMenu.qml} | 38 +- interface/resources/qml/Root.qml | 30 ++ interface/resources/qml/RootMenu.qml | 34 ++ interface/resources/qml/TestRoot.qml | 3 +- libraries/ui/src/MenuConstants.h | 467 +----------------- libraries/ui/src/OffscreenQmlDialog.h | 8 +- libraries/ui/src/OffscreenUi.cpp | 41 +- libraries/ui/src/OffscreenUi.h | 14 +- tests/ui/main.qml | 161 ++++++ tests/ui/qml/ButtonPage.qml | 128 +++++ tests/ui/qml/InputPage.qml | 114 +++++ tests/ui/qml/ProgressPage.qml | 90 ++++ tests/ui/qml/UI.js | 45 ++ tests/ui/src/main.cpp | 82 ++- 14 files changed, 724 insertions(+), 531 deletions(-) rename interface/resources/qml/{Menu.qml => HifiMenu.qml} (94%) create mode 100644 interface/resources/qml/RootMenu.qml create mode 100644 tests/ui/main.qml create mode 100644 tests/ui/qml/ButtonPage.qml create mode 100644 tests/ui/qml/InputPage.qml create mode 100644 tests/ui/qml/ProgressPage.qml create mode 100644 tests/ui/qml/UI.js diff --git a/interface/resources/qml/Menu.qml b/interface/resources/qml/HifiMenu.qml similarity index 94% rename from interface/resources/qml/Menu.qml rename to interface/resources/qml/HifiMenu.qml index f76b9485fb..8bdda71558 100644 --- a/interface/resources/qml/Menu.qml +++ b/interface/resources/qml/HifiMenu.qml @@ -5,10 +5,10 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -Hifi.Menu { +Hifi.HifiMenu { id: root anchors.fill: parent - objectName: "Menu" + objectName: "HifiMenu" enabled: false opacity: 0.0 property int animationDuration: 200 @@ -16,7 +16,7 @@ Hifi.Menu { onEnabledChanged: { if (enabled && columns.length == 0) { - pushColumn(menu.items); + pushColumn(rootMenu.items); } opacity = enabled ? 1.0 : 0.0 if (enabled) { @@ -202,19 +202,23 @@ Hifi.Menu { } function popColumn() { - if (columns.length > 1) { + if (columns.length > 0) { var curColumn = columns.pop(); console.log(curColumn); curColumn.visible = false; curColumn.destroy(); models.pop(); - curColumn = lastColumn(); - curColumn.enabled = true; - curColumn.opacity = 1.0; - curColumn.forceActiveFocus(); - } else { + } + + if (columns.length == 0) { enabled = false; - } + return; + } + + curColumn = lastColumn(); + curColumn.enabled = true; + curColumn.opacity = 1.0; + curColumn.forceActiveFocus(); } function selectItem(source) { @@ -223,7 +227,6 @@ Hifi.Menu { pushColumn(source.items) break; case 1: - console.log("Triggering " + source.text); source.trigger() enabled = false break; @@ -233,12 +236,9 @@ Hifi.Menu { } function reset() { - console.log("Resettting") - while (columns.length > 1) { + while (columns.length > 0) { popColumn(); } - lastColumn().children[0].currentIndex = -1 - console.log(lastColumn().children[0]) } /* @@ -367,9 +367,13 @@ Hifi.Menu { MouseArea { anchors.fill: parent id: mouseArea - acceptedButtons: Qt.RightButton + acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { - root.popColumn(); + if(mouseArea.pressedButtons & Qt.RightButton) { + root.popColumn(); + } else { + root.enabled = false; + } } } } diff --git a/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index b2db7d18bf..92ad922cce 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -6,5 +6,35 @@ import QtQuick 2.3 Root { id: root anchors.fill: parent + Item { + Menu { + objectName: "rootMenu" + Menu { + title: "File" + MenuItem { + text: "Test" + checkable: true + } + MenuItem { + text: "Quit" + } + } + Menu { + title: "Edit" + MenuItem { + text: "Copy" + } + MenuItem { + text: "Cut" + } + MenuItem { + text: "Paste" + } + MenuItem { + text: "Undo" + } + } + } + } } diff --git a/interface/resources/qml/RootMenu.qml b/interface/resources/qml/RootMenu.qml new file mode 100644 index 0000000000..46cae2cf4a --- /dev/null +++ b/interface/resources/qml/RootMenu.qml @@ -0,0 +1,34 @@ +import QtQuick 2.4 +import QtQuick.Controls 1.3 + +Item { + Menu { + id: rootMenuFooBar + objectName: "rootMenu" + Menu { + title: "File" + MenuItem { + text: "Test" + checkable: true + } + MenuItem { + text: "Quit" + } + } + Menu { + title: "Edit" + MenuItem { + text: "Copy" + } + MenuItem { + text: "Cut" + } + MenuItem { + text: "Paste" + } + MenuItem { + text: "Undo" + } + } + } +} diff --git a/interface/resources/qml/TestRoot.qml b/interface/resources/qml/TestRoot.qml index 0f48939935..0b0f890361 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -1,5 +1,6 @@ import Hifi 1.0 import QtQuick 2.3 +import QtQuick.Controls 1.3 // Import local folder last so that our own control customizations override // the built in ones import "controls" @@ -19,7 +20,7 @@ Root { console.log("Completed root") root.forceActiveFocus() } - + Button { id: messageBox anchors.right: createDialog.left diff --git a/libraries/ui/src/MenuConstants.h b/libraries/ui/src/MenuConstants.h index e2d50b10a6..e986410c5b 100644 --- a/libraries/ui/src/MenuConstants.h +++ b/libraries/ui/src/MenuConstants.h @@ -17,480 +17,19 @@ #include #include "OffscreenQmlDialog.h" -namespace MenuOption { - const QString AboutApp = "About Interface"; - const QString AddRemoveFriends = "Add/Remove Friends..."; - const QString AddressBar = "Show Address Bar"; - const QString AlignForearmsWithWrists = "Align Forearms with Wrists"; - const QString AlternateIK = "Alternate IK"; - const QString AmbientOcclusion = "Ambient Occlusion"; - const QString Animations = "Animations..."; - const QString Atmosphere = "Atmosphere"; - const QString Attachments = "Attachments..."; - const QString AudioNoiseReduction = "Audio Noise Reduction"; - const QString AudioScope = "Show Scope"; - const QString AudioScopeFiftyFrames = "Fifty"; - const QString AudioScopeFiveFrames = "Five"; - const QString AudioScopeFrames = "Display Frames"; - const QString AudioScopePause = "Pause Scope"; - const QString AudioScopeTwentyFrames = "Twenty"; - const QString AudioStats = "Audio Stats"; - const QString AudioStatsShowInjectedStreams = "Audio Stats Show Injected Streams"; - const QString BandwidthDetails = "Bandwidth Details"; - const QString BlueSpeechSphere = "Blue Sphere While Speaking"; - const QString BookmarkLocation = "Bookmark Location"; - const QString Bookmarks = "Bookmarks"; - const QString CascadedShadows = "Cascaded"; - const QString CachesSize = "RAM Caches Size"; - const QString Chat = "Chat..."; - const QString Collisions = "Collisions"; - const QString Console = "Console..."; - const QString ControlWithSpeech = "Control With Speech"; - const QString CopyAddress = "Copy Address to Clipboard"; - const QString CopyPath = "Copy Path to Clipboard"; - const QString DDEFaceRegression = "DDE Face Regression"; - const QString DDEFiltering = "DDE Filtering"; - const QString DecreaseAvatarSize = "Decrease Avatar Size"; - const QString DeleteBookmark = "Delete Bookmark..."; - const QString DisableActivityLogger = "Disable Activity Logger"; - const QString DisableLightEntities = "Disable Light Entities"; - const QString DisableNackPackets = "Disable NACK Packets"; - const QString DiskCacheEditor = "Disk Cache Editor"; - const QString DisplayHands = "Show Hand Info"; - const QString DisplayHandTargets = "Show Hand Targets"; - const QString DisplayModelBounds = "Display Model Bounds"; - const QString DisplayModelTriangles = "Display Model Triangles"; - const QString DisplayModelElementChildProxies = "Display Model Element Children"; - const QString DisplayModelElementProxy = "Display Model Element Bounds"; - const QString DisplayDebugTimingDetails = "Display Timing Details"; - const QString DontDoPrecisionPicking = "Don't Do Precision Picking"; - const QString DontFadeOnOctreeServerChanges = "Don't Fade In/Out on Octree Server Changes"; - const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; - const QString EchoLocalAudio = "Echo Local Audio"; - const QString EchoServerAudio = "Echo Server Audio"; - const QString EditEntitiesHelp = "Edit Entities Help..."; - const QString Enable3DTVMode = "Enable 3DTV Mode"; - const QString EnableCharacterController = "Enable avatar collisions"; - const QString EnableGlowEffect = "Enable Glow Effect (Warning: Poor Oculus Performance)"; - const QString EnableVRMode = "Enable VR Mode"; - const QString ExpandMyAvatarSimulateTiming = "Expand /myAvatar/simulation"; - const QString ExpandMyAvatarTiming = "Expand /myAvatar"; - const QString ExpandOtherAvatarTiming = "Expand /otherAvatar"; - const QString ExpandPaintGLTiming = "Expand /paintGL"; - const QString ExpandUpdateTiming = "Expand /update"; - const QString Faceshift = "Faceshift"; - const QString FilterSixense = "Smooth Sixense Movement"; - const QString FirstPerson = "First Person"; - const QString FrameTimer = "Show Timer"; - const QString Fullscreen = "Fullscreen"; - const QString FullscreenMirror = "Fullscreen Mirror"; - const QString GlowWhenSpeaking = "Glow When Speaking"; - const QString NamesAboveHeads = "Names Above Heads"; - const QString GoToUser = "Go To User"; - const QString HMDTools = "HMD Tools"; - const QString IncreaseAvatarSize = "Increase Avatar Size"; - const QString KeyboardMotorControl = "Enable Keyboard Motor Control"; - const QString LeapMotionOnHMD = "Leap Motion on HMD"; - const QString LoadScript = "Open and Run Script File..."; - const QString LoadScriptURL = "Open and Run Script from URL..."; - const QString LoadRSSDKFile = "Load .rssdk file"; - const QString LodTools = "LOD Tools"; - const QString Login = "Login"; - const QString Log = "Log"; - const QString LowVelocityFilter = "Low Velocity Filter"; - const QString Mirror = "Mirror"; - const QString MuteAudio = "Mute Microphone"; - const QString MuteEnvironment = "Mute Environment"; - const QString NoFaceTracking = "None"; - const QString OctreeStats = "Entity Statistics"; - const QString OffAxisProjection = "Off-Axis Projection"; - const QString OnlyDisplayTopTen = "Only Display Top Ten"; - const QString PackageModel = "Package Model..."; - const QString Pair = "Pair"; - const QString PipelineWarnings = "Log Render Pipeline Warnings"; - const QString Preferences = "Preferences..."; - const QString Quit = "Quit"; - const QString ReloadAllScripts = "Reload All Scripts"; - const QString RenderBoundingCollisionShapes = "Show Bounding Collision Shapes"; - const QString RenderFocusIndicator = "Show Eye Focus"; - const QString RenderHeadCollisionShapes = "Show Head Collision Shapes"; - const QString RenderLookAtVectors = "Show Look-at Vectors"; - const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes"; - const QString RenderTargetFramerate = "Framerate"; - const QString RenderTargetFramerateUnlimited = "Unlimited"; - const QString RenderTargetFramerate60 = "60"; - const QString RenderTargetFramerate50 = "50"; - const QString RenderTargetFramerate40 = "40"; - const QString RenderTargetFramerate30 = "30"; - const QString RenderTargetFramerateVSyncOn = "V-Sync On"; - const QString RenderResolution = "Scale Resolution"; - const QString RenderResolutionOne = "1"; - const QString RenderResolutionTwoThird = "2/3"; - const QString RenderResolutionHalf = "1/2"; - const QString RenderResolutionThird = "1/3"; - const QString RenderResolutionQuarter = "1/4"; - const QString RenderAmbientLight = "Ambient Light"; - const QString RenderAmbientLightGlobal = "Global"; - const QString RenderAmbientLight0 = "OLD_TOWN_SQUARE"; - const QString RenderAmbientLight1 = "GRACE_CATHEDRAL"; - const QString RenderAmbientLight2 = "EUCALYPTUS_GROVE"; - const QString RenderAmbientLight3 = "ST_PETERS_BASILICA"; - const QString RenderAmbientLight4 = "UFFIZI_GALLERY"; - const QString RenderAmbientLight5 = "GALILEOS_TOMB"; - const QString RenderAmbientLight6 = "VINE_STREET_KITCHEN"; - const QString RenderAmbientLight7 = "BREEZEWAY"; - const QString RenderAmbientLight8 = "CAMPUS_SUNSET"; - const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET"; - const QString ResetAvatarSize = "Reset Avatar Size"; - const QString ResetDDETracking = "Reset DDE Tracking"; - const QString ResetSensors = "Reset Sensors"; - const QString RunningScripts = "Running Scripts"; - const QString RunTimingTests = "Run Timing Tests"; - const QString ScriptEditor = "Script Editor..."; - const QString ScriptedMotorControl = "Enable Scripted Motor Control"; - const QString ShowBordersEntityNodes = "Show Entity Nodes"; - const QString ShowIKConstraints = "Show IK Constraints"; - const QString SimpleShadows = "Simple"; - const QString SixenseEnabled = "Enable Hydra Support"; - const QString SixenseMouseInput = "Enable Sixense Mouse Input"; - const QString SixenseLasers = "Enable Sixense UI Lasers"; - 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"; - const QString ToolWindow = "Tool Window"; - const QString TransmitterDrive = "Transmitter Drive"; - const QString TurnWithHead = "Turn using Head"; - const QString UseAudioForMouth = "Use Audio for Mouth"; - const QString VisibleToEveryone = "Everyone"; - const QString VisibleToFriends = "Friends"; - const QString VisibleToNoOne = "No one"; - const QString Wireframe = "Wireframe"; -} - - class HifiMenu : public QQuickItem { Q_OBJECT QML_DIALOG_DECL - Q_PROPERTY(QString aboutApp READ aboutApp CONSTANT) - Q_PROPERTY(QString addRemoveFriends READ addRemoveFriends CONSTANT) - Q_PROPERTY(QString addressBar READ addressBar CONSTANT) - Q_PROPERTY(QString alignForearmsWithWrists READ alignForearmsWithWrists CONSTANT) - Q_PROPERTY(QString alternateIK READ alternateIK CONSTANT) - Q_PROPERTY(QString ambientOcclusion READ ambientOcclusion CONSTANT) - Q_PROPERTY(QString animations READ animations CONSTANT) - Q_PROPERTY(QString atmosphere READ atmosphere CONSTANT) - Q_PROPERTY(QString attachments READ attachments CONSTANT) - Q_PROPERTY(QString audioNoiseReduction READ audioNoiseReduction CONSTANT) - Q_PROPERTY(QString audioScope READ audioScope CONSTANT) - Q_PROPERTY(QString audioScopeFiftyFrames READ audioScopeFiftyFrames CONSTANT) - Q_PROPERTY(QString audioScopeFiveFrames READ audioScopeFiveFrames CONSTANT) - Q_PROPERTY(QString audioScopeFrames READ audioScopeFrames CONSTANT) - Q_PROPERTY(QString audioScopePause READ audioScopePause CONSTANT) - Q_PROPERTY(QString audioScopeTwentyFrames READ audioScopeTwentyFrames CONSTANT) - Q_PROPERTY(QString audioStats READ audioStats CONSTANT) - Q_PROPERTY(QString audioStatsShowInjectedStreams READ audioStatsShowInjectedStreams CONSTANT) - Q_PROPERTY(QString bandwidthDetails READ bandwidthDetails CONSTANT) - Q_PROPERTY(QString blueSpeechSphere READ blueSpeechSphere CONSTANT) - Q_PROPERTY(QString bookmarkLocation READ bookmarkLocation CONSTANT) - Q_PROPERTY(QString bookmarks READ bookmarks CONSTANT) - Q_PROPERTY(QString cascadedShadows READ cascadedShadows CONSTANT) - Q_PROPERTY(QString cachesSize READ cachesSize CONSTANT) - Q_PROPERTY(QString chat READ chat CONSTANT) - Q_PROPERTY(QString collisions READ collisions CONSTANT) - Q_PROPERTY(QString console READ console CONSTANT) - Q_PROPERTY(QString controlWithSpeech READ controlWithSpeech CONSTANT) - Q_PROPERTY(QString copyAddress READ copyAddress CONSTANT) - Q_PROPERTY(QString copyPath READ copyPath CONSTANT) - Q_PROPERTY(QString ddeFaceRegression READ ddeFaceRegression CONSTANT) - Q_PROPERTY(QString ddeFiltering READ ddeFiltering CONSTANT) - Q_PROPERTY(QString decreaseAvatarSize READ decreaseAvatarSize CONSTANT) - Q_PROPERTY(QString deleteBookmark READ deleteBookmark CONSTANT) - Q_PROPERTY(QString disableActivityLogger READ disableActivityLogger CONSTANT) - Q_PROPERTY(QString disableLightEntities READ disableLightEntities CONSTANT) - Q_PROPERTY(QString disableNackPackets READ disableNackPackets CONSTANT) - Q_PROPERTY(QString diskCacheEditor READ diskCacheEditor CONSTANT) - Q_PROPERTY(QString displayHands READ displayHands CONSTANT) - Q_PROPERTY(QString displayHandTargets READ displayHandTargets CONSTANT) - Q_PROPERTY(QString displayModelBounds READ displayModelBounds CONSTANT) - Q_PROPERTY(QString displayModelTriangles READ displayModelTriangles CONSTANT) - Q_PROPERTY(QString displayModelElementChildProxies READ displayModelElementChildProxies CONSTANT) - Q_PROPERTY(QString displayModelElementProxy READ displayModelElementProxy CONSTANT) - Q_PROPERTY(QString displayDebugTimingDetails READ displayDebugTimingDetails CONSTANT) - Q_PROPERTY(QString dontDoPrecisionPicking READ dontDoPrecisionPicking CONSTANT) - Q_PROPERTY(QString dontFadeOnOctreeServerChanges READ dontFadeOnOctreeServerChanges CONSTANT) - Q_PROPERTY(QString dontRenderEntitiesAsScene READ dontRenderEntitiesAsScene CONSTANT) - Q_PROPERTY(QString echoLocalAudio READ echoLocalAudio CONSTANT) - Q_PROPERTY(QString echoServerAudio READ echoServerAudio CONSTANT) - Q_PROPERTY(QString editEntitiesHelp READ editEntitiesHelp CONSTANT) - Q_PROPERTY(QString enable3DTVMode READ enable3DTVMode CONSTANT) - Q_PROPERTY(QString enableCharacterController READ enableCharacterController CONSTANT) - Q_PROPERTY(QString enableGlowEffect READ enableGlowEffect CONSTANT) - Q_PROPERTY(QString enableVRMode READ enableVRMode CONSTANT) - Q_PROPERTY(QString expandMyAvatarSimulateTiming READ expandMyAvatarSimulateTiming CONSTANT) - Q_PROPERTY(QString expandMyAvatarTiming READ expandMyAvatarTiming CONSTANT) - Q_PROPERTY(QString expandOtherAvatarTiming READ expandOtherAvatarTiming CONSTANT) - Q_PROPERTY(QString expandPaintGLTiming READ expandPaintGLTiming CONSTANT) - Q_PROPERTY(QString expandUpdateTiming READ expandUpdateTiming CONSTANT) - Q_PROPERTY(QString faceshift READ faceshift CONSTANT) - Q_PROPERTY(QString filterSixense READ filterSixense CONSTANT) - Q_PROPERTY(QString firstPerson READ firstPerson CONSTANT) - Q_PROPERTY(QString frameTimer READ frameTimer CONSTANT) - Q_PROPERTY(QString fullscreen READ fullscreen CONSTANT) - Q_PROPERTY(QString fullscreenMirror READ fullscreenMirror CONSTANT) - Q_PROPERTY(QString glowWhenSpeaking READ glowWhenSpeaking CONSTANT) - Q_PROPERTY(QString namesAboveHeads READ namesAboveHeads CONSTANT) - Q_PROPERTY(QString goToUser READ goToUser CONSTANT) - Q_PROPERTY(QString hmdTools READ hmdTools CONSTANT) - Q_PROPERTY(QString increaseAvatarSize READ increaseAvatarSize CONSTANT) - Q_PROPERTY(QString keyboardMotorControl READ keyboardMotorControl CONSTANT) - Q_PROPERTY(QString leapMotionOnHMD READ leapMotionOnHMD CONSTANT) - Q_PROPERTY(QString loadScript READ loadScript CONSTANT) - Q_PROPERTY(QString loadScriptURL READ loadScriptURL CONSTANT) - Q_PROPERTY(QString loadRSSDKFile READ loadRSSDKFile CONSTANT) - Q_PROPERTY(QString lodTools READ lodTools CONSTANT) - Q_PROPERTY(QString login READ login CONSTANT) - Q_PROPERTY(QString log READ log CONSTANT) - Q_PROPERTY(QString lowVelocityFilter READ lowVelocityFilter CONSTANT) - Q_PROPERTY(QString mirror READ mirror CONSTANT) - Q_PROPERTY(QString muteAudio READ muteAudio CONSTANT) - Q_PROPERTY(QString muteEnvironment READ muteEnvironment CONSTANT) - Q_PROPERTY(QString noFaceTracking READ noFaceTracking CONSTANT) - Q_PROPERTY(QString octreeStats READ octreeStats CONSTANT) - Q_PROPERTY(QString offAxisProjection READ offAxisProjection CONSTANT) - Q_PROPERTY(QString onlyDisplayTopTen READ onlyDisplayTopTen CONSTANT) - Q_PROPERTY(QString packageModel READ packageModel CONSTANT) - Q_PROPERTY(QString pair READ pair CONSTANT) - Q_PROPERTY(QString pipelineWarnings READ pipelineWarnings CONSTANT) - Q_PROPERTY(QString preferences READ preferences CONSTANT) - Q_PROPERTY(QString quit READ quit CONSTANT) - Q_PROPERTY(QString reloadAllScripts READ reloadAllScripts CONSTANT) - Q_PROPERTY(QString renderBoundingCollisionShapes READ renderBoundingCollisionShapes CONSTANT) - Q_PROPERTY(QString renderFocusIndicator READ renderFocusIndicator CONSTANT) - Q_PROPERTY(QString renderHeadCollisionShapes READ renderHeadCollisionShapes CONSTANT) - Q_PROPERTY(QString renderLookAtVectors READ renderLookAtVectors CONSTANT) - Q_PROPERTY(QString renderSkeletonCollisionShapes READ renderSkeletonCollisionShapes CONSTANT) - Q_PROPERTY(QString renderTargetFramerate READ renderTargetFramerate CONSTANT) - Q_PROPERTY(QString renderTargetFramerateUnlimited READ renderTargetFramerateUnlimited CONSTANT) - Q_PROPERTY(QString renderTargetFramerate60 READ renderTargetFramerate60 CONSTANT) - Q_PROPERTY(QString renderTargetFramerate50 READ renderTargetFramerate50 CONSTANT) - Q_PROPERTY(QString renderTargetFramerate40 READ renderTargetFramerate40 CONSTANT) - Q_PROPERTY(QString renderTargetFramerate30 READ renderTargetFramerate30 CONSTANT) - Q_PROPERTY(QString renderTargetFramerateVSyncOn READ renderTargetFramerateVSyncOn CONSTANT) - Q_PROPERTY(QString renderResolution READ renderResolution CONSTANT) - Q_PROPERTY(QString renderResolutionOne READ renderResolutionOne CONSTANT) - Q_PROPERTY(QString renderResolutionTwoThird READ renderResolutionTwoThird CONSTANT) - Q_PROPERTY(QString renderResolutionHalf READ renderResolutionHalf CONSTANT) - Q_PROPERTY(QString renderResolutionThird READ renderResolutionThird CONSTANT) - Q_PROPERTY(QString renderResolutionQuarter READ renderResolutionQuarter CONSTANT) - Q_PROPERTY(QString renderAmbientLight READ renderAmbientLight CONSTANT) - Q_PROPERTY(QString renderAmbientLightGlobal READ renderAmbientLightGlobal CONSTANT) - Q_PROPERTY(QString renderAmbientLight0 READ renderAmbientLight0 CONSTANT) - Q_PROPERTY(QString renderAmbientLight1 READ renderAmbientLight1 CONSTANT) - Q_PROPERTY(QString renderAmbientLight2 READ renderAmbientLight2 CONSTANT) - Q_PROPERTY(QString renderAmbientLight3 READ renderAmbientLight3 CONSTANT) - Q_PROPERTY(QString renderAmbientLight4 READ renderAmbientLight4 CONSTANT) - Q_PROPERTY(QString renderAmbientLight5 READ renderAmbientLight5 CONSTANT) - Q_PROPERTY(QString renderAmbientLight6 READ renderAmbientLight6 CONSTANT) - Q_PROPERTY(QString renderAmbientLight7 READ renderAmbientLight7 CONSTANT) - Q_PROPERTY(QString renderAmbientLight8 READ renderAmbientLight8 CONSTANT) - Q_PROPERTY(QString renderAmbientLight9 READ renderAmbientLight9 CONSTANT) - Q_PROPERTY(QString resetAvatarSize READ resetAvatarSize CONSTANT) - Q_PROPERTY(QString resetDDETracking READ resetDDETracking CONSTANT) - Q_PROPERTY(QString resetSensors READ resetSensors CONSTANT) - Q_PROPERTY(QString runningScripts READ runningScripts CONSTANT) - Q_PROPERTY(QString runTimingTests READ runTimingTests CONSTANT) - Q_PROPERTY(QString scriptEditor READ scriptEditor CONSTANT) - Q_PROPERTY(QString scriptedMotorControl READ scriptedMotorControl CONSTANT) - Q_PROPERTY(QString showBordersEntityNodes READ showBordersEntityNodes CONSTANT) - Q_PROPERTY(QString showIKConstraints READ showIKConstraints CONSTANT) - Q_PROPERTY(QString simpleShadows READ simpleShadows CONSTANT) - Q_PROPERTY(QString sixenseEnabled READ sixenseEnabled CONSTANT) - Q_PROPERTY(QString sixenseMouseInput READ sixenseMouseInput CONSTANT) - Q_PROPERTY(QString sixenseLasers READ sixenseLasers CONSTANT) - Q_PROPERTY(QString shiftHipsForIdleAnimations READ shiftHipsForIdleAnimations CONSTANT) - Q_PROPERTY(QString stars READ stars CONSTANT) - Q_PROPERTY(QString stats READ stats CONSTANT) - Q_PROPERTY(QString stereoAudio READ stereoAudio CONSTANT) - Q_PROPERTY(QString stopAllScripts READ stopAllScripts CONSTANT) - Q_PROPERTY(QString suppressShortTimings READ suppressShortTimings CONSTANT) - Q_PROPERTY(QString testPing READ testPing CONSTANT) - Q_PROPERTY(QString toolWindow READ toolWindow CONSTANT) - Q_PROPERTY(QString transmitterDrive READ transmitterDrive CONSTANT) - Q_PROPERTY(QString turnWithHead READ turnWithHead CONSTANT) - Q_PROPERTY(QString useAudioForMouth READ useAudioForMouth CONSTANT) - Q_PROPERTY(QString visibleToEveryone READ visibleToEveryone CONSTANT) - Q_PROPERTY(QString visibleToFriends READ visibleToFriends CONSTANT) - Q_PROPERTY(QString visibleToNoOne READ visibleToNoOne CONSTANT) - Q_PROPERTY(QString wireframe READ wireframe CONSTANT) - -public: - - const QString & aboutApp() { return MenuOption::AboutApp; } - const QString & addRemoveFriends() { return MenuOption::AddRemoveFriends; } - const QString & addressBar() { return MenuOption::AddressBar; } - const QString & alignForearmsWithWrists() { return MenuOption::AlignForearmsWithWrists; } - const QString & alternateIK() { return MenuOption::AlternateIK; } - const QString & ambientOcclusion() { return MenuOption::AmbientOcclusion; } - const QString & animations() { return MenuOption::Animations; } - const QString & atmosphere() { return MenuOption::Atmosphere; } - const QString & attachments() { return MenuOption::Attachments; } - const QString & audioNoiseReduction() { return MenuOption::AudioNoiseReduction; } - const QString & audioScope() { return MenuOption::AudioScope; } - const QString & audioScopeFiftyFrames() { return MenuOption::AudioScopeFiftyFrames; } - const QString & audioScopeFiveFrames() { return MenuOption::AudioScopeFiveFrames; } - const QString & audioScopeFrames() { return MenuOption::AudioScopeFrames; } - const QString & audioScopePause() { return MenuOption::AudioScopePause; } - const QString & audioScopeTwentyFrames() { return MenuOption::AudioScopeTwentyFrames; } - const QString & audioStats() { return MenuOption::AudioStats; } - const QString & audioStatsShowInjectedStreams() { return MenuOption::AudioStatsShowInjectedStreams; } - const QString & bandwidthDetails() { return MenuOption::BandwidthDetails; } - const QString & blueSpeechSphere() { return MenuOption::BlueSpeechSphere; } - const QString & bookmarkLocation() { return MenuOption::BookmarkLocation; } - const QString & bookmarks() { return MenuOption::Bookmarks; } - const QString & cascadedShadows() { return MenuOption::CascadedShadows; } - const QString & cachesSize() { return MenuOption::CachesSize; } - const QString & chat() { return MenuOption::Chat; } - const QString & collisions() { return MenuOption::Collisions; } - const QString & console() { return MenuOption::Console; } - const QString & controlWithSpeech() { return MenuOption::ControlWithSpeech; } - const QString & copyAddress() { return MenuOption::CopyAddress; } - const QString & copyPath() { return MenuOption::CopyPath; } - const QString & ddeFaceRegression() { return MenuOption::DDEFaceRegression; } - const QString & ddeFiltering() { return MenuOption::DDEFiltering; } - const QString & decreaseAvatarSize() { return MenuOption::DecreaseAvatarSize; } - const QString & deleteBookmark() { return MenuOption::DeleteBookmark; } - const QString & disableActivityLogger() { return MenuOption::DisableActivityLogger; } - const QString & disableLightEntities() { return MenuOption::DisableLightEntities; } - const QString & disableNackPackets() { return MenuOption::DisableNackPackets; } - const QString & diskCacheEditor() { return MenuOption::DiskCacheEditor; } - const QString & displayHands() { return MenuOption::DisplayHands; } - const QString & displayHandTargets() { return MenuOption::DisplayHandTargets; } - const QString & displayModelBounds() { return MenuOption::DisplayModelBounds; } - const QString & displayModelTriangles() { return MenuOption::DisplayModelTriangles; } - const QString & displayModelElementChildProxies() { return MenuOption::DisplayModelElementChildProxies; } - const QString & displayModelElementProxy() { return MenuOption::DisplayModelElementProxy; } - const QString & displayDebugTimingDetails() { return MenuOption::DisplayDebugTimingDetails; } - const QString & dontDoPrecisionPicking() { return MenuOption::DontDoPrecisionPicking; } - const QString & dontFadeOnOctreeServerChanges() { return MenuOption::DontFadeOnOctreeServerChanges; } - const QString & dontRenderEntitiesAsScene() { return MenuOption::DontRenderEntitiesAsScene; } - const QString & echoLocalAudio() { return MenuOption::EchoLocalAudio; } - const QString & echoServerAudio() { return MenuOption::EchoServerAudio; } - const QString & editEntitiesHelp() { return MenuOption::EditEntitiesHelp; } - const QString & enable3DTVMode() { return MenuOption::Enable3DTVMode; } - const QString & enableCharacterController() { return MenuOption::EnableCharacterController; } - const QString & enableGlowEffect() { return MenuOption::EnableGlowEffect; } - const QString & enableVRMode() { return MenuOption::EnableVRMode; } - const QString & expandMyAvatarSimulateTiming() { return MenuOption::ExpandMyAvatarSimulateTiming; } - const QString & expandMyAvatarTiming() { return MenuOption::ExpandMyAvatarTiming; } - const QString & expandOtherAvatarTiming() { return MenuOption::ExpandOtherAvatarTiming; } - const QString & expandPaintGLTiming() { return MenuOption::ExpandPaintGLTiming; } - const QString & expandUpdateTiming() { return MenuOption::ExpandUpdateTiming; } - const QString & faceshift() { return MenuOption::Faceshift; } - const QString & filterSixense() { return MenuOption::FilterSixense; } - const QString & firstPerson() { return MenuOption::FirstPerson; } - const QString & frameTimer() { return MenuOption::FrameTimer; } - const QString & fullscreen() { return MenuOption::Fullscreen; } - const QString & fullscreenMirror() { return MenuOption::FullscreenMirror; } - const QString & glowWhenSpeaking() { return MenuOption::GlowWhenSpeaking; } - const QString & namesAboveHeads() { return MenuOption::NamesAboveHeads; } - const QString & goToUser() { return MenuOption::GoToUser; } - const QString & hmdTools() { return MenuOption::HMDTools; } - const QString & increaseAvatarSize() { return MenuOption::IncreaseAvatarSize; } - const QString & keyboardMotorControl() { return MenuOption::KeyboardMotorControl; } - const QString & leapMotionOnHMD() { return MenuOption::LeapMotionOnHMD; } - const QString & loadScript() { return MenuOption::LoadScript; } - const QString & loadScriptURL() { return MenuOption::LoadScriptURL; } - const QString & loadRSSDKFile() { return MenuOption::LoadRSSDKFile; } - const QString & lodTools() { return MenuOption::LodTools; } - const QString & login() { return MenuOption::Login; } - const QString & log() { return MenuOption::Log; } - const QString & lowVelocityFilter() { return MenuOption::LowVelocityFilter; } - const QString & mirror() { return MenuOption::Mirror; } - const QString & muteAudio() { return MenuOption::MuteAudio; } - const QString & muteEnvironment() { return MenuOption::MuteEnvironment; } - const QString & noFaceTracking() { return MenuOption::NoFaceTracking; } - const QString & octreeStats() { return MenuOption::OctreeStats; } - const QString & offAxisProjection() { return MenuOption::OffAxisProjection; } - const QString & onlyDisplayTopTen() { return MenuOption::OnlyDisplayTopTen; } - const QString & packageModel() { return MenuOption::PackageModel; } - const QString & pair() { return MenuOption::Pair; } - const QString & pipelineWarnings() { return MenuOption::PipelineWarnings; } - const QString & preferences() { return MenuOption::Preferences; } - const QString & quit() { return MenuOption::Quit; } - const QString & reloadAllScripts() { return MenuOption::ReloadAllScripts; } - const QString & renderBoundingCollisionShapes() { return MenuOption::RenderBoundingCollisionShapes; } - const QString & renderFocusIndicator() { return MenuOption::RenderFocusIndicator; } - const QString & renderHeadCollisionShapes() { return MenuOption::RenderHeadCollisionShapes; } - const QString & renderLookAtVectors() { return MenuOption::RenderLookAtVectors; } - const QString & renderSkeletonCollisionShapes() { return MenuOption::RenderSkeletonCollisionShapes; } - const QString & renderTargetFramerate() { return MenuOption::RenderTargetFramerate; } - const QString & renderTargetFramerateUnlimited() { return MenuOption::RenderTargetFramerateUnlimited; } - const QString & renderTargetFramerate60() { return MenuOption::RenderTargetFramerate60; } - const QString & renderTargetFramerate50() { return MenuOption::RenderTargetFramerate50; } - const QString & renderTargetFramerate40() { return MenuOption::RenderTargetFramerate40; } - const QString & renderTargetFramerate30() { return MenuOption::RenderTargetFramerate30; } - const QString & renderTargetFramerateVSyncOn() { return MenuOption::RenderTargetFramerateVSyncOn; } - const QString & renderResolution() { return MenuOption::RenderResolution; } - const QString & renderResolutionOne() { return MenuOption::RenderResolutionOne; } - const QString & renderResolutionTwoThird() { return MenuOption::RenderResolutionTwoThird; } - const QString & renderResolutionHalf() { return MenuOption::RenderResolutionHalf; } - const QString & renderResolutionThird() { return MenuOption::RenderResolutionThird; } - const QString & renderResolutionQuarter() { return MenuOption::RenderResolutionQuarter; } - const QString & renderAmbientLight() { return MenuOption::RenderAmbientLight; } - const QString & renderAmbientLightGlobal() { return MenuOption::RenderAmbientLightGlobal; } - const QString & renderAmbientLight0() { return MenuOption::RenderAmbientLight0; } - const QString & renderAmbientLight1() { return MenuOption::RenderAmbientLight1; } - const QString & renderAmbientLight2() { return MenuOption::RenderAmbientLight2; } - const QString & renderAmbientLight3() { return MenuOption::RenderAmbientLight3; } - const QString & renderAmbientLight4() { return MenuOption::RenderAmbientLight4; } - const QString & renderAmbientLight5() { return MenuOption::RenderAmbientLight5; } - const QString & renderAmbientLight6() { return MenuOption::RenderAmbientLight6; } - const QString & renderAmbientLight7() { return MenuOption::RenderAmbientLight7; } - const QString & renderAmbientLight8() { return MenuOption::RenderAmbientLight8; } - const QString & renderAmbientLight9() { return MenuOption::RenderAmbientLight9; } - const QString & resetAvatarSize() { return MenuOption::ResetAvatarSize; } - const QString & resetDDETracking() { return MenuOption::ResetDDETracking; } - const QString & resetSensors() { return MenuOption::ResetSensors; } - const QString & runningScripts() { return MenuOption::RunningScripts; } - const QString & runTimingTests() { return MenuOption::RunTimingTests; } - const QString & scriptEditor() { return MenuOption::ScriptEditor; } - const QString & scriptedMotorControl() { return MenuOption::ScriptedMotorControl; } - const QString & showBordersEntityNodes() { return MenuOption::ShowBordersEntityNodes; } - const QString & showIKConstraints() { return MenuOption::ShowIKConstraints; } - const QString & simpleShadows() { return MenuOption::SimpleShadows; } - const QString & sixenseEnabled() { return MenuOption::SixenseEnabled; } - const QString & sixenseMouseInput() { return MenuOption::SixenseMouseInput; } - const QString & sixenseLasers() { return MenuOption::SixenseLasers; } - const QString & shiftHipsForIdleAnimations() { return MenuOption::ShiftHipsForIdleAnimations; } - const QString & stars() { return MenuOption::Stars; } - const QString & stats() { return MenuOption::Stats; } - const QString & stereoAudio() { return MenuOption::StereoAudio; } - const QString & stopAllScripts() { return MenuOption::StopAllScripts; } - const QString & suppressShortTimings() { return MenuOption::SuppressShortTimings; } - const QString & testPing() { return MenuOption::TestPing; } - const QString & toolWindow() { return MenuOption::ToolWindow; } - const QString & transmitterDrive() { return MenuOption::TransmitterDrive; } - const QString & turnWithHead() { return MenuOption::TurnWithHead; } - const QString & useAudioForMouth() { return MenuOption::UseAudioForMouth; } - const QString & visibleToEveryone() { return MenuOption::VisibleToEveryone; } - const QString & visibleToFriends() { return MenuOption::VisibleToFriends; } - const QString & visibleToNoOne() { return MenuOption::VisibleToNoOne; } - const QString & wireframe() { return MenuOption::Wireframe; } - -public slots: - void onTriggeredByName(const QString & name); - void onToggledByName(const QString & name); public: HifiMenu(QQuickItem * parent = nullptr); static void setToggleAction(const QString & name, std::function f); static void setTriggerAction(const QString & name, std::function f); +private slots: + void onTriggeredByName(const QString & name); + void onToggledByName(const QString & name); private: static QHash> triggerActions; static QHash> toggleActions; diff --git a/libraries/ui/src/OffscreenQmlDialog.h b/libraries/ui/src/OffscreenQmlDialog.h index c6c95981a6..7b7c83ad65 100644 --- a/libraries/ui/src/OffscreenQmlDialog.h +++ b/libraries/ui/src/OffscreenQmlDialog.h @@ -23,8 +23,8 @@ private: \ static const QUrl QML; \ public: \ static void registerType(); \ - static void show(std::function f = [](QQmlContext*, QQuickItem*) {}); \ - static void toggle(std::function f = [](QQmlContext*, QQuickItem*) {}); \ + static void show(std::function f = [](QQmlContext*, QObject*) {}); \ + static void toggle(std::function f = [](QQmlContext*, QObject*) {}); \ private: #define QML_DIALOG_DEF(x) \ @@ -35,12 +35,12 @@ private: qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \ } \ \ - void x::show(std::function f) { \ + void x::show(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ offscreenUi->show(QML, NAME, f); \ } \ \ - void x::toggle(std::function f) { \ + void x::toggle(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ offscreenUi->toggle(QML, NAME, f); \ } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index b14951f054..d44a5dd282 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -132,22 +132,27 @@ void OffscreenUi::resize(const QSize& newSize) { doneCurrent(); } -QQmlContext* OffscreenUi::qmlContext() { - if (nullptr == _rootItem) { - return _qmlComponent->creationContext(); - } - return QQmlEngine::contextForObject(_rootItem); +QQuickItem* OffscreenUi::getRootItem() { + return _rootItem; } +//QQmlContext* OffscreenUi::qmlContext() { +// if (nullptr == _rootItem) { +// return _qmlComponent->creationContext(); +// } +// return QQmlEngine::contextForObject(_rootItem); +//} + void OffscreenUi::setBaseUrl(const QUrl& baseUrl) { _qmlEngine->setBaseUrl(baseUrl); } -void OffscreenUi::load(const QUrl& qmlSource, std::function f) { +void 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] { + connect(_qmlComponent, &QQmlComponent::statusChanged, this, + [this, f](QQmlComponent::Status){ finishQmlLoad(f); }); else { @@ -168,8 +173,8 @@ void OffscreenUi::requestRender() { } } -void OffscreenUi::finishQmlLoad(std::function f) { - disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad); +void OffscreenUi::finishQmlLoad(std::function f) { + disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, 0); if (_qmlComponent->isError()) { QList errorList = _qmlComponent->errors(); foreach(const QQmlError &error, errorList) { @@ -178,7 +183,8 @@ void OffscreenUi::finishQmlLoad(std::function f return; } - QObject* newObject = _qmlComponent->create(); + QQmlContext * newContext = new QQmlContext(_qmlEngine, qApp); + QObject* newObject = _qmlComponent->beginCreate(newContext); if (_qmlComponent->isError()) { QList errorList = _qmlComponent->errors(); foreach(const QQmlError &error, errorList) @@ -189,6 +195,9 @@ void OffscreenUi::finishQmlLoad(std::function f return; } + f(newContext, newObject); + _qmlComponent->completeCreate(); + QQuickItem* newItem = qobject_cast(newObject); if (!newItem) { qWarning("run: Not a QQuickItem"); @@ -203,7 +212,6 @@ void OffscreenUi::finishQmlLoad(std::function f // Make sure we make items focusable (critical for // supporting keyboard shortcuts) newItem->setFlag(QQuickItem::ItemIsFocusScope, true); - f(_qmlEngine->contextForObject(newItem), newItem); if (!_rootItem) { // The root item is ready. Associate it with the window. _rootItem = newItem; @@ -215,7 +223,6 @@ void OffscreenUi::finishQmlLoad(std::function f QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership); newItem->setParent(_rootItem); newItem->setParentItem(_rootItem); - newItem->setEnabled(true); } } @@ -397,22 +404,22 @@ void OffscreenUi::setProxyWindow(QWindow* window) { _renderControl->_renderWindow = window; } -void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { +void OffscreenUi::show(const QUrl& url, const QString& name, std::function f) { QQuickItem* item = _rootItem->findChild(name); // First load? if (!item) { load(url, f); - return; + item = _rootItem->findChild(name); } item->setEnabled(true); } -void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function f) { +void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function f) { QQuickItem* item = _rootItem->findChild(name); // First load? if (!item) { load(url, f); - return; + item = _rootItem->findChild(name); } item->setEnabled(!item->isEnabled()); } @@ -421,7 +428,7 @@ void OffscreenUi::messageBox(const QString& title, const QString& text, ButtonCallback callback, QMessageBox::Icon icon, QMessageBox::StandardButtons buttons) { - MessageDialog::show([=](QQmlContext*ctx, QQuickItem*item) { + MessageDialog::show([=](QQmlContext*ctx, QObject*item) { MessageDialog * pDialog = item->findChild(); pDialog->setIcon((MessageDialog::Icon)icon); pDialog->setTitle(title); diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index d04eb63bff..33fb46b28a 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -92,16 +92,16 @@ public: virtual ~OffscreenUi(); void create(QOpenGLContext* context); void resize(const QSize& size); - void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QQuickItem*) {}); - void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QQuickItem*) {}) { + void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { load(QUrl(qmlSourceFile), f); } - void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {}); - void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QQuickItem*) {}); + void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); + void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); void setBaseUrl(const QUrl& baseUrl); void addImportPath(const QString& path); - QQmlContext* qmlContext(); - + //QQmlContext* getQmlContext(); + QQuickItem* getRootItem(); void pause(); void resume(); bool isPaused() const; @@ -143,7 +143,7 @@ protected: private slots: void updateQuick(); - void finishQmlLoad(std::function f); + void finishQmlLoad(std::function f); public slots: void requestUpdate(); diff --git a/tests/ui/main.qml b/tests/ui/main.qml new file mode 100644 index 0000000000..89dac3d1af --- /dev/null +++ b/tests/ui/main.qml @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Dialogs 1.1 +import QtQuick.Controls 1.2 +import "qml/UI.js" as UI +import "qml" +//import "/Users/bdavis/Git/hifi/interface/resources/qml" + +Item { + anchors.fill: parent + visible: true + //title: "Qt Quick Controls Gallery" + + MessageDialog { + id: aboutDialog + icon: StandardIcon.Information + title: "About" + text: "Qt Quick Controls Gallery" + informativeText: "This example demonstrates most of the available Qt Quick Controls." + } + + Action { + id: copyAction + text: "&Copy" + shortcut: StandardKey.Copy + iconName: "edit-copy" + enabled: (!!activeFocusItem && !!activeFocusItem["copy"]) + onTriggered: activeFocusItem.copy() + } + + Action { + id: cutAction + text: "Cu&t" + shortcut: StandardKey.Cut + iconName: "edit-cut" + enabled: (!!activeFocusItem && !!activeFocusItem["cut"]) + onTriggered: activeFocusItem.cut() + } + + Action { + id: pasteAction + text: "&Paste" + shortcut: StandardKey.Paste + iconName: "edit-paste" + enabled: (!!activeFocusItem && !!activeFocusItem["paste"]) + onTriggered: activeFocusItem.paste() + } + +// toolBar: ToolBar { +// RowLayout { +// anchors.fill: parent +// anchors.margins: spacing +// Label { +// text: UI.label +// } +// Item { Layout.fillWidth: true } +// CheckBox { +// id: enabler +// text: "Enabled" +// checked: true +// } +// } +// } + +// menuBar: MenuBar { +// Menu { +// title: "&File" +// MenuItem { +// text: "E&xit" +// shortcut: StandardKey.Quit +// onTriggered: Qt.quit() +// } +// } +// Menu { +// title: "&Edit" +// visible: tabView.currentIndex == 2 +// MenuItem { action: cutAction } +// MenuItem { action: copyAction } +// MenuItem { action: pasteAction } +// } +// Menu { +// title: "&Help" +// MenuItem { +// text: "About..." +// onTriggered: aboutDialog.open() +// } +// } +// } + + TabView { + id: tabView + + anchors.fill: parent + anchors.margins: UI.margin + tabPosition: UI.tabPosition + + Layout.minimumWidth: 360 + Layout.minimumHeight: 360 + Layout.preferredWidth: 480 + Layout.preferredHeight: 640 + + Tab { + title: "Buttons" + ButtonPage { + enabled: enabler.checked + } + } + Tab { + title: "Progress" + ProgressPage { + enabled: enabler.checked + } + } + Tab { + title: "Input" + InputPage { + enabled: enabler.checked + } + } + } +} diff --git a/tests/ui/qml/ButtonPage.qml b/tests/ui/qml/ButtonPage.qml new file mode 100644 index 0000000000..0ed7e2d6ad --- /dev/null +++ b/tests/ui/qml/ButtonPage.qml @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, grid.implicitWidth + 2 * grid.rowSpacing) + height: Math.max(page.viewport.height, grid.implicitHeight + 2 * grid.columnSpacing) + + GridLayout { + id: grid + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: grid.rowSpacing + anchors.rightMargin: grid.rowSpacing + anchors.topMargin: grid.columnSpacing + + columns: page.width < page.height ? 1 : 2 + + GroupBox { + title: "Button" + Layout.fillWidth: true + Layout.columnSpan: grid.columns + RowLayout { + anchors.fill: parent + Button { text: "OK"; isDefault: true } + Button { text: "Cancel" } + Item { Layout.fillWidth: true } + Button { + text: "Attach" + menu: Menu { + MenuItem { text: "Image" } + MenuItem { text: "Document" } + } + } + } + } + + GroupBox { + title: "CheckBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + CheckBox { text: "E-mail"; checked: true } + CheckBox { text: "Calendar"; checked: true } + CheckBox { text: "Contacts" } + } + } + + GroupBox { + title: "RadioButton" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ExclusiveGroup { id: radioGroup } + RadioButton { text: "Portrait"; exclusiveGroup: radioGroup } + RadioButton { text: "Landscape"; exclusiveGroup: radioGroup } + RadioButton { text: "Automatic"; exclusiveGroup: radioGroup; checked: true } + } + } + + GroupBox { + title: "Switch" + Layout.fillWidth: true + Layout.columnSpan: grid.columns + ColumnLayout { + anchors.fill: parent + RowLayout { + Label { text: "Wi-Fi"; Layout.fillWidth: true } + Switch { checked: true } + } + RowLayout { + Label { text: "Bluetooth"; Layout.fillWidth: true } + Switch { checked: false } + } + } + } + } + } +} diff --git a/tests/ui/qml/InputPage.qml b/tests/ui/qml/InputPage.qml new file mode 100644 index 0000000000..cb1878d023 --- /dev/null +++ b/tests/ui/qml/InputPage.qml @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) + height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) + + ColumnLayout { + id: column + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: column.spacing + + GroupBox { + title: "TextField" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + TextField { placeholderText: "..."; Layout.fillWidth: true; z: 1 } + TextField { placeholderText: "Password"; echoMode: TextInput.Password; Layout.fillWidth: true } + } + } + + GroupBox { + title: "ComboBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ComboBox { + model: Qt.fontFamilies() + Layout.fillWidth: true + } + ComboBox { + editable: true + model: ListModel { + id: listModel + ListElement { text: "Apple" } + ListElement { text: "Banana" } + ListElement { text: "Coconut" } + ListElement { text: "Orange" } + } + onAccepted: { + if (find(currentText) === -1) { + listModel.append({text: editText}) + currentIndex = find(editText) + } + } + Layout.fillWidth: true + } + } + } + + GroupBox { + title: "SpinBox" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + SpinBox { value: 99; Layout.fillWidth: true; z: 1 } + SpinBox { decimals: 2; Layout.fillWidth: true } + } + } + } + } +} diff --git a/tests/ui/qml/ProgressPage.qml b/tests/ui/qml/ProgressPage.qml new file mode 100644 index 0000000000..a1fa596f79 --- /dev/null +++ b/tests/ui/qml/ProgressPage.qml @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.2 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.2 + +ScrollView { + id: page + + horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff + + Item { + id: content + + width: Math.max(page.viewport.width, column.implicitWidth + 2 * column.spacing) + height: Math.max(page.viewport.height, column.implicitHeight + 2 * column.spacing) + + ColumnLayout { + id: column + + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + anchors.margins: column.spacing + + GroupBox { + title: "ProgressBar" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + ProgressBar { indeterminate: true; Layout.fillWidth: true } + ProgressBar { value: slider.value; Layout.fillWidth: true } + } + } + + GroupBox { + title: "Slider" + Layout.fillWidth: true + ColumnLayout { + anchors.fill: parent + Slider { id: slider; value: 0.5; Layout.fillWidth: true } + } + } + + GroupBox { + title: "BusyIndicator" + Layout.fillWidth: true + BusyIndicator { running: true } + } + } + } +} diff --git a/tests/ui/qml/UI.js b/tests/ui/qml/UI.js new file mode 100644 index 0000000000..0286ac56a6 --- /dev/null +++ b/tests/ui/qml/UI.js @@ -0,0 +1,45 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Quick Controls module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** You may use this file under the terms of the BSD license as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names +** of its contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +.pragma library + +var margin = 2 +var tabPosition = Qt.TopEdge +var label = "" diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 00d0bc80fd..e84e5674c1 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -83,6 +84,17 @@ const QString & getQmlDir() { return dir; } +const QString & getTestQmlDir() { + static QString dir; + if (dir.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + dir = path.cleanPath(path.absoluteFilePath("../")) + "/"; + qDebug() << "Qml Test Path: " << dir; + } + return dir; +} + // Create a simple OpenGL window that renders text in various ways class QTestWindow : public QWindow, private QOpenGLFunctions { Q_OBJECT @@ -95,6 +107,8 @@ class QTestWindow : public QWindow, private QOpenGLFunctions { int testQmlTexture{ 0 }; public: + QObject * rootMenu; + QTestWindow() { _timer.setInterval(1); connect(&_timer, &QTimer::timeout, [=] { @@ -144,18 +158,6 @@ public: auto offscreenUi = DependencyManager::get(); offscreenUi->create(_context); - - makeCurrent(); - - offscreenUi->setProxyWindow(this); - setFramePosition(QPoint(-1000, 0)); - resize(QSize(800, 600)); - - offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); - offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->addImportPath(getQmlDir()); - offscreenUi->addImportPath("."); - connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) { offscreenUi->lockTexture(textureId); assert(!glGetError()); @@ -165,10 +167,25 @@ public: offscreenUi->releaseTexture(oldTexture); } }); + + makeCurrent(); + + offscreenUi->setProxyWindow(this); + setFramePosition(QPoint(-1000, 0)); + resize(QSize(800, 600)); + +#ifdef QML_CONTROL_GALLERY + offscreenUi->setBaseUrl(QUrl::fromLocalFile(getTestQmlDir())); + offscreenUi->load(QUrl("main.qml")); +#else + offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); + offscreenUi->load(QUrl("TestRoot.qml")); + offscreenUi->load(QUrl("RootMenu.qml")); +#endif installEventFilter(offscreenUi.data()); - HifiMenu::setTriggerAction(MenuOption::Quit, [] { - QApplication::quit(); - }); + //HifiMenu::setTriggerAction(MenuOption::Quit, [] { + // QApplication::quit(); + //}); offscreenUi->resume(); _timer.start(); } @@ -216,15 +233,30 @@ protected: resizeWindow(ev->size()); } + + static QObject * addMenu(QObject * parent, const QString & text) { + // FIXME add more checking here to ensure no name conflicts + QVariant returnedValue; + QMetaObject::invokeMethod(parent, "addMenu", + Q_RETURN_ARG(QVariant, returnedValue), + Q_ARG(QVariant, text)); + QObject * result = returnedValue.value(); + return result; + } + 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(); - //DependencyManager::get()->toggle(QString("TestDialog.qml"), "TestDialog"); - //DependencyManager::get()->toggle(QString("MenuTest.qml"), "MenuTest"); - //HifiMenu::toggle(); + auto offscreenUi = DependencyManager::get(); + rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + QObject * result = addMenu(rootMenu, "Test 3"); + result->setParent(rootMenu); + qDebug() << "Added " << result; + if (menuContext) { + menuContext->setContextProperty("rootMenu", rootMenu); + } } break; case Qt::Key_K: @@ -236,15 +268,23 @@ protected: break; case Qt::Key_J: if (event->modifiers() & Qt::CTRL) { + auto offscreenUi = DependencyManager::get(); + rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + QMetaObject::invokeMethod(rootMenu, "popup"); } break; } QWindow::keyPressEvent(event); } - + QQmlContext* menuContext{ nullptr }; void keyReleaseEvent(QKeyEvent *event) { if (_altPressed && Qt::Key_Alt == event->key()) { - HifiMenu::toggle(); + HifiMenu::toggle([=](QQmlContext* context, QObject* newItem) { + auto offscreenUi = DependencyManager::get(); + rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + menuContext = context; + menuContext->setContextProperty("rootMenu", rootMenu); + }); } } From f17387fab40ed225157362b6db154c2937e5acb8 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Apr 2015 21:23:21 -0700 Subject: [PATCH 08/33] Working on menus --- BUILD_WIN.md | 6 +- interface/resources/qml/Root.qml | 30 - interface/resources/qml/RootMenu.qml | 27 +- interface/src/Application.cpp | 9 + interface/src/Menu.cpp | 1061 +++++++++----------------- interface/src/Menu.h | 61 +- libraries/ui/src/HifiMenu.cpp | 221 ++++++ libraries/ui/src/HifiMenu.h | 72 ++ libraries/ui/src/MenuConstants.cpp | 50 -- libraries/ui/src/MenuConstants.h | 42 - libraries/ui/src/OffscreenUi.cpp | 8 +- libraries/ui/src/OffscreenUi.h | 30 + tests/ui/src/main.cpp | 56 +- 13 files changed, 749 insertions(+), 924 deletions(-) create mode 100644 libraries/ui/src/HifiMenu.cpp create mode 100644 libraries/ui/src/HifiMenu.h delete mode 100644 libraries/ui/src/MenuConstants.cpp delete mode 100644 libraries/ui/src/MenuConstants.h 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/interface/resources/qml/Root.qml b/interface/resources/qml/Root.qml index 92ad922cce..b2db7d18bf 100644 --- a/interface/resources/qml/Root.qml +++ b/interface/resources/qml/Root.qml @@ -6,35 +6,5 @@ import QtQuick 2.3 Root { id: root anchors.fill: parent - Item { - Menu { - objectName: "rootMenu" - Menu { - title: "File" - MenuItem { - text: "Test" - checkable: true - } - MenuItem { - text: "Quit" - } - } - Menu { - title: "Edit" - MenuItem { - text: "Copy" - } - MenuItem { - text: "Cut" - } - MenuItem { - text: "Paste" - } - MenuItem { - text: "Undo" - } - } - } - } } diff --git a/interface/resources/qml/RootMenu.qml b/interface/resources/qml/RootMenu.qml index 46cae2cf4a..dc2e36f89a 100644 --- a/interface/resources/qml/RootMenu.qml +++ b/interface/resources/qml/RootMenu.qml @@ -3,32 +3,7 @@ import QtQuick.Controls 1.3 Item { Menu { - id: rootMenuFooBar + id: root objectName: "rootMenu" - Menu { - title: "File" - MenuItem { - text: "Test" - checkable: true - } - MenuItem { - text: "Quit" - } - } - Menu { - title: "Edit" - MenuItem { - text: "Copy" - } - MenuItem { - text: "Cut" - } - MenuItem { - text: "Paste" - } - MenuItem { - text: "Undo" - } - } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c435ca2f0b..d281282fc8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -786,6 +786,7 @@ void Application::initializeUi() { offscreenUi->setProxyWindow(_window->windowHandle()); offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/")); offscreenUi->load("Root.qml"); + offscreenUi->load("RootMenu.qml"); offscreenUi->setMouseTranslator([this](const QPointF& p){ if (OculusManager::isConnected()) { glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p)); @@ -1079,9 +1080,11 @@ bool Application::eventFilter(QObject* object, QEvent* event) { } static bool _altPressed; +static bool _ctrlPressed; void Application::keyPressEvent(QKeyEvent* event) { _altPressed = event->key() == Qt::Key_Alt; + _ctrlPressed = event->key() == Qt::Key_Control; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1336,6 +1339,12 @@ void Application::keyReleaseEvent(QKeyEvent* event) { if (event->key() == Qt::Key_Alt && _altPressed) { Menu::toggle(); } + if (event->key() == Qt::Key_Control && _ctrlPressed) { + auto offscreenUi = DependencyManager::get(); + auto rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); + QMetaObject::invokeMethod(rootMenu, "popup"); + } + _ctrlPressed = event->key() == Qt::Key_Control; _keysPressed.remove(event->key()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 026af1442f..529fca5e66 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include @@ -52,34 +52,43 @@ HifiAction::HifiAction(const QString & menuOption) : _menuOption(menuOption) { // qFatal("Not implemented"); //} -void HifiAction::setChecked(bool) { - qFatal("Not implemented"); +void HifiAction::setChecked(bool checked) { + Menu::getInstance()->setChecked(_menuOption, checked); } -void HifiAction::setVisible(bool) { - qFatal("Not implemented"); +void HifiAction::setVisible(bool visible) { + QObject* result = Menu::getInstance()->findMenuObject(_menuOption); + if (result) { + result->setProperty("visible", visible); + } } const QString & HifiAction::shortcut() const { - qFatal("Not implemented"); - return ""; + static const QString NONE; + QObject* result = Menu::getInstance()->findMenuObject(_menuOption); + if (!result) { + return NONE; + } + QObject* shortcut = result->property("shortcut").value(); + if (!shortcut) { + return NONE; + } + shortcut->dumpObjectInfo(); + return NONE; } -void HifiAction::setText(const QString &) { - qFatal("Not implemented"); +void HifiAction::setText(const QString & text) { + Menu::getInstance()->setText(_menuOption, text); } void HifiAction::setTriggerAction(std::function f) { - qFatal("Not implemented"); + Menu::getInstance()->setTriggerAction(_menuOption, f); } void HifiAction::setToggleAction(std::function f) { - qFatal("Not implemented"); + Menu::getInstance()->setToggleAction(_menuOption, f); } -HIFI_QML_DEF(Menu) - - Menu* Menu::_instance = nullptr; Menu* Menu::getInstance() { @@ -88,15 +97,11 @@ Menu* Menu::getInstance() { static QMutex menuInstanceMutex; withLock(menuInstanceMutex, [] { if (!_instance) { - OffscreenUi * offscreenUi = DependencyManager::get().data(); - QQmlContext * qmlContext = offscreenUi->qmlContext(); - qmlContext->setContextProperty("foo", QVariant::fromValue(foo)); -// qmlContext->setContextProperty("presetsModel", QVariant::fromValue(dataList)); - + qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - load([&](QQmlContext *, QQuickItem* item) { - _instance = dynamic_cast(item); - }); + Menu::load(); + auto uiRoot = DependencyManager::get()->getRootItem(); + _instance = uiRoot->findChild(NAME); if (!_instance) { qFatal("Could not load menu QML"); } else { @@ -108,380 +113,362 @@ Menu* Menu::getInstance() { return _instance; } -Menu::Menu(QQuickItem * parent) : QQuickItem(parent) { +Menu::Menu(QQuickItem * parent) : HifiMenu(parent) { } -QObject * _rootMenu; -static const QString FILE_MENU{ "File" }; -static const QString EDIT_MENU{ "Edit" }; -static const QString TOOLS_MENU{ "Tools" }; -static const QString AVATAR_MENU{ "Avatar" }; - - -static const QString MENU_SUFFIX{ "__Menu" }; - -QObject * addMenu(QObject * parent, const QString & text) { - // FIXME add more checking here to ensure no name conflicts - QVariant returnedValue; - QMetaObject::invokeMethod(parent, "addMenu", - 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; - QMetaObject::invokeMethod(parent, "addItem", - Q_RETURN_ARG(QQuickMenuItem*, returnedValue), - Q_ARG(QString, text)); - QObject* result = reinterpret_cast(returnedValue); - if (result) { - result->setObjectName(text + MENU_SUFFIX); - } - return result; -} void Menu::init() { - _rootMenu = property("menu").value(); - QObject * fileMenu = ::addMenu(_rootMenu, FILE_MENU); - auto dialogsManager = DependencyManager::get(); AccountManager& accountManager = AccountManager::getInstance(); - + static const QString ROOT_MENU; { - ::addItem(fileMenu, MenuOption::Login); - - // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::profileChanged, - dialogsManager.data(), &DialogsManager::toggleLoginDialog); - connect(&accountManager, &AccountManager::logoutComplete, - dialogsManager.data(), &DialogsManager::toggleLoginDialog); - } - + static const QString FILE_MENU{ "File" }; + addMenu(ROOT_MENU, FILE_MENU); + { + addMenuItem(FILE_MENU, MenuOption::Login); + // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item + connect(&accountManager, &AccountManager::profileChanged, + dialogsManager.data(), &DialogsManager::toggleLoginDialog); + connect(&accountManager, &AccountManager::logoutComplete, + dialogsManager.data(), &DialogsManager::toggleLoginDialog); + } #ifdef Q_OS_MAC - addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); + addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); #endif - { - static const QString SCRIPTS_MENU{ "Scripts" }; - addMenu(FILE_MENU, SCRIPTS_MENU); - //Qt::CTRL | Qt::Key_O - addMenuItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { - qApp->loadDialog(); - }); - //Qt::CTRL | Qt::SHIFT | Qt::Key_O - addMenuItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { - qApp->loadScriptURLDialog(); - }); - addMenuItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { - qApp->stopAllScripts(); - }); - //Qt::CTRL | Qt::Key_R, - addMenuItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { - qApp->reloadAllScripts(); - }); - // Qt::CTRL | Qt::Key_J, - addMenuItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { - qApp->toggleRunningScriptsWidget(); + { + static const QString SCRIPTS_MENU{ "Scripts" }; + addMenu(FILE_MENU, SCRIPTS_MENU); + //Qt::CTRL | Qt::Key_O + addMenuItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { + qApp->loadDialog(); + }); + //Qt::CTRL | Qt::SHIFT | Qt::Key_O + addMenuItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { + qApp->loadScriptURLDialog(); + }); + addMenuItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { + qApp->stopAllScripts(); + }); + //Qt::CTRL | Qt::Key_R, + addMenuItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { + qApp->reloadAllScripts(); + }); + // Qt::CTRL | Qt::Key_J, + addMenuItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { + qApp->toggleRunningScriptsWidget(); + }); + } + + { + static const QString LOCATION_MENU{ "Location" }; + addMenu(FILE_MENU, LOCATION_MENU); + qApp->getBookmarks()->setupMenus(LOCATION_MENU); + //Qt::CTRL | Qt::Key_L + addMenuItem(LOCATION_MENU, MenuOption::AddressBar, [=] { + auto dialogsManager = DependencyManager::get(); + dialogsManager->toggleAddressBar(); + }); + addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { + auto addressManager = DependencyManager::get(); + addressManager->copyAddress(); + }); + addMenuItem(LOCATION_MENU, MenuOption::CopyPath, [=] { + auto addressManager = DependencyManager::get(); + addressManager->copyPath(); + }); + } + + // Qt::CTRL | Qt::Key_Q + // QAction::QuitRole + addMenuItem(FILE_MENU, MenuOption::Quit, [=] { + qApp->quit(); }); } + { - static const QString LOCATION_MENU{ "Location" }; - addMenu(FILE_MENU, LOCATION_MENU); - qApp->getBookmarks()->setupMenus(LOCATION_MENU); - //Qt::CTRL | Qt::Key_L - addMenuItem(LOCATION_MENU, MenuOption::AddressBar, [=] { - auto dialogsManager = DependencyManager::get(); - dialogsManager->toggleAddressBar(); - }); - addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyAddress(); - }); - addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyPath(); - }); - } + static const QString EDIT_MENU{ "Edit" }; + addMenu(ROOT_MENU, EDIT_MENU); #if 0 - - addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, - addressManager.data(), SLOT(copyAddress())); - addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, - addressManager.data(), SLOT(copyPath())); + QUndoStack* undoStack = qApp->getUndoStack(); + QAction* undoAction = undoStack->createUndoAction(editMenu); + undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, undoAction); - addActionToQMenuAndActionHash(fileMenu, - MenuOption::Quit, - Qt::CTRL | Qt::Key_Q, - qApp, - SLOT(quit()), - QAction::QuitRole); + QAction* redoAction = undoStack->createRedoAction(editMenu); + redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, redoAction); +#endif + // Qt::CTRL | Qt::Key_Comma + // QAction::PreferencesRole + addMenuItem(EDIT_MENU, MenuOption::Preferences, [=] { + dialogsManager->editPreferences(); + }); + addMenuItem(EDIT_MENU, MenuOption::Animations, [=] { + dialogsManager->editAnimations(); + }); + } - QMenu* editMenu = addMenu("Edit"); - - QUndoStack* undoStack = qApp->getUndoStack(); - QAction* undoAction = undoStack->createUndoAction(editMenu); - undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); - addActionToQMenuAndActionHash(editMenu, undoAction); - - QAction* redoAction = undoStack->createRedoAction(editMenu); - redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); - addActionToQMenuAndActionHash(editMenu, redoAction); - - addActionToQMenuAndActionHash(editMenu, - MenuOption::Preferences, - Qt::CTRL | Qt::Key_Comma, - dialogsManager.data(), - SLOT(editPreferences()), - QAction::PreferencesRole); - - addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, - dialogsManager.data(), SLOT(editAttachments())); - addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, - dialogsManager.data(), SLOT(editAnimations())); - - QMenu* toolsMenu = addMenu("Tools"); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, - dialogsManager.data(), SLOT(showScriptEditor())); + { + static const QString TOOLS_MENU{ "Tools" }; + addMenu(ROOT_MENU, TOOLS_MENU); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) - auto speechRecognizer = DependencyManager::get(); - QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech, - Qt::CTRL | Qt::SHIFT | Qt::Key_C, - speechRecognizer->getEnabled(), - speechRecognizer.data(), - SLOT(setEnabled(bool))); - connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); + auto speechRecognizer = DependencyManager::get(); + //Qt::CTRL | Qt::SHIFT | Qt::Key_C + addMenuItem(TOOLS_MENU, MenuOption::ControlWithSpeech); + setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); + connect(speechRecognizer.data(), &SpeechRecognizer::enabledUpdated, [=] { + setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); + }); #endif - - addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, - 0, // QML Qt::Key_Backslash, - dialogsManager.data(), SLOT(showIRCLink())); - addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0, - qApp, SLOT(showFriendsWindow())); + // Qt::ALT | Qt::Key_S, + addMenuItem(TOOLS_MENU, MenuOption::ScriptEditor, [=] { + dialogsManager->showScriptEditor(); + }); + // QML Qt::Key_Backslash, + addMenuItem(TOOLS_MENU, MenuOption::Chat, [=] { + dialogsManager->showIRCLink(); + }); + addMenuItem(TOOLS_MENU, MenuOption::AddRemoveFriends, [=] { + qApp->showFriendsWindow(); + }); - QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); - { - QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); - auto discoverabilityManager = DependencyManager::get(); +#if 0 + QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); + { + QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); + auto discoverabilityManager = DependencyManager::get(); - QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToEveryone); + QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToEveryone); - QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToFriends); + QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToFriends); - QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToNoOne); + QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToNoOne); - connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, - discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); + connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, + discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); + } +#endif + //Qt::CTRL | Qt::ALT | Qt::Key_T, + addMenuItem(TOOLS_MENU, MenuOption::ToolWindow, [=] { +// dialogsManager->toggleToolWindow(); + }); + + //Qt::CTRL | Qt::ALT | Qt::Key_J, + addMenuItem(TOOLS_MENU, MenuOption::Console, [=] { + DependencyManager::get()->toggleConsole(); + }); + + // QML Qt::Key_Apostrophe, + addMenuItem(TOOLS_MENU, MenuOption::ResetSensors, [=] { + qApp->resetSensors(); + }); + + addMenuItem(TOOLS_MENU, MenuOption::PackageModel, [=] { + qApp->packageModel(); + }); } - addActionToQMenuAndActionHash(toolsMenu, - MenuOption::ToolWindow, - Qt::CTRL | Qt::ALT | Qt::Key_T, - dialogsManager.data(), - SLOT(toggleToolWindow())); - - addActionToQMenuAndActionHash(toolsMenu, - MenuOption::Console, - Qt::CTRL | Qt::ALT | Qt::Key_J, - DependencyManager::get().data(), - SLOT(toggleConsole())); - - addActionToQMenuAndActionHash(toolsMenu, - MenuOption::ResetSensors, - 0, // QML Qt::Key_Apostrophe, - qApp, - SLOT(resetSensors())); - - addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, - qApp, SLOT(packageModel())); - - QMenu* avatarMenu = addMenu("Avatar"); - QObject* avatar = DependencyManager::get()->getMyAvatar(); - - QMenu* avatarSizeMenu = avatarMenu->addMenu("Size"); - addActionToQMenuAndActionHash(avatarSizeMenu, - MenuOption::IncreaseAvatarSize, - 0, // QML Qt::Key_Plus, - avatar, - SLOT(increaseSize())); - addActionToQMenuAndActionHash(avatarSizeMenu, - MenuOption::DecreaseAvatarSize, - 0, // QML Qt::Key_Minus, - avatar, - SLOT(decreaseSize())); - addActionToQMenuAndActionHash(avatarSizeMenu, - MenuOption::ResetAvatarSize, - 0, // QML Qt::Key_Equal, - avatar, - SLOT(resetSize())); - - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl, - Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehavior())); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true, - avatar, SLOT(updateMotionBehavior())); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true, - avatar, SLOT(updateMotionBehavior())); - addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, - avatar, SLOT(updateMotionBehavior())); - - QMenu* viewMenu = addMenu("View"); - - addCheckableActionToQMenuAndActionHash(viewMenu, - MenuOption::Fullscreen, -#ifdef Q_OS_MAC - Qt::CTRL | Qt::META | Qt::Key_F, -#else - Qt::CTRL | Qt::Key_F, -#endif - false, - qApp, - SLOT(setFullscreen(bool))); - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, - 0, // QML Qt::Key_P, - true, qApp, SLOT(cameraMenuChanged())); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, - 0, //QML Qt::SHIFT | Qt::Key_H, - true); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, - 0, // QML Qt::Key_H, - false, qApp, SLOT(cameraMenuChanged())); - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, -#ifdef Q_OS_MAC - Qt::META | Qt::Key_H, -#else - Qt::CTRL | Qt::Key_H, -#endif - false, - dialogsManager.data(), - SLOT(hmdTools(bool))); - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, - false, - qApp, - SLOT(setEnableVRMode(bool))); - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, - false, - qApp, - SLOT(setEnable3DTVMode(bool))); - - - QMenu* nodeBordersMenu = viewMenu->addMenu("Server Borders"); - NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay(); - addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, - Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, - &nodeBounds, SLOT(setShowEntityNodes(bool))); - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); - - - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, - 0); // QML Qt::Key_Slash); - addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats); - addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, - Qt::CTRL | Qt::SHIFT | Qt::Key_L, - qApp, SLOT(toggleLogDialog())); - addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, - dialogsManager.data(), SLOT(bandwidthDetails())); - addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, - dialogsManager.data(), SLOT(octreeStatsDetails())); - - - QMenu* developerMenu = addMenu("Developer"); - - QMenu* 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); - QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); - ambientLightGroup->setExclusive(true); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight0, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight1, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight2, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight3, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight4, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight5, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight6, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); - ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); - - QMenu* 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); - QActionGroup* framerateGroup = new QActionGroup(framerateMenu); - framerateGroup->setExclusive(true); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); - framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); + static const QString AVATAR_MENU{ "Avatar" }; + addMenu(ROOT_MENU, AVATAR_MENU); + auto avatar = DependencyManager::get()->getMyAvatar(); + { + static const QString SIZE_MENU{ "Size" }; + addMenu(AVATAR_MENU, SIZE_MENU); + // QML Qt::Key_Plus, + addMenuItem(SIZE_MENU, MenuOption::IncreaseAvatarSize, [=] { + avatar->increaseSize(); + }); + // QML Qt::Key_Minus, + addMenuItem(SIZE_MENU, MenuOption::DecreaseAvatarSize, [=] { + avatar->decreaseSize(); + }); + // QML Qt::Key_Equal, + addMenuItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { + avatar->resetSize(); + }); -#if defined(Q_OS_MAC) -#else - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, - qApp, SLOT(setVSyncEnabled())); -#endif + addMenuItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { + avatar->resetSize(); + }); + } + + //Qt::CTRL | Qt::SHIFT | Qt::Key_K + addCheckableMenuItem(AVATAR_MENU, MenuOption::KeyboardMotorControl, true, [=](bool) { + avatar->updateMotionBehavior(); + }); + addCheckableMenuItem(AVATAR_MENU, MenuOption::ScriptedMotorControl, true); + addCheckableMenuItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); + addCheckableMenuItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); + addCheckableMenuItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); + addCheckableMenuItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, [=](bool) { + avatar->updateMotionBehavior(); + }); + addCheckableMenuItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, [=](bool) { + avatar->updateMotionBehavior(); + }); } + { + static const QString VIEW_MENU{ "View" }; + addMenu(ROOT_MENU, VIEW_MENU); - QMenu* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); - QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); - resolutionGroup->setExclusive(true); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); - resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); + // Mac Qt::CTRL | Qt::META | Qt::Key_F, + // Win32/Linux Qt::CTRL | Qt::Key_F, + addCheckableMenuItem(VIEW_MENU, MenuOption::Fullscreen, false, [=](bool checked) { +// qApp->setFullscreen(checked); + }); + // QML Qt::Key_P, + addCheckableMenuItem(VIEW_MENU, MenuOption::FirstPerson, true, [=](bool checked) { +// qApp->cameraMenuChanged(); + }); + //QML Qt::SHIFT | Qt::Key_H, + addCheckableMenuItem(VIEW_MENU, MenuOption::Mirror, true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, - 0, // QML Qt::Key_Asterisk, - true); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, - DependencyManager::get().data(), SLOT(toggleGlowEffect(bool))); + // QML Qt::Key_H, + addCheckableMenuItem(VIEW_MENU, MenuOption::FullscreenMirror, true, [=](bool checked) { +// qApp->cameraMenuChanged(); + }); - addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); - addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, - 0, // QML Qt::SHIFT | Qt::Key_L, - dialogsManager.data(), SLOT(lodTools())); + // Mac Qt::META | Qt::Key_H, + // Win32/Linux Qt::CTRL | Qt::Key_H, + addCheckableMenuItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { + dialogsManager->hmdTools(checked); + }); + addCheckableMenuItem(VIEW_MENU, MenuOption::EnableVRMode, false, [=](bool checked) { +// qApp->setEnableVRMode(checked); + }); + addCheckableMenuItem(VIEW_MENU, MenuOption::Enable3DTVMode, false, [=](bool checked) { +// qApp->setEnable3DTVMode(checked); + }); - QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar"); + { + static const QString BORDER_MENU{ "View" }; + addMenu(VIEW_MENU, BORDER_MENU); + // Qt::CTRL | Qt::SHIFT | Qt::Key_1 + addCheckableMenuItem(BORDER_MENU, MenuOption::ShowBordersEntityNodes, false, [=](bool checked) { + qApp->getNodeBoundsDisplay().setShowEntityNodes(checked); + }); + } + addCheckableMenuItem(VIEW_MENU, MenuOption::OffAxisProjection, false); + addCheckableMenuItem(VIEW_MENU, MenuOption::TurnWithHead, false); + // QML Qt::Key_Slash + addCheckableMenuItem(VIEW_MENU, MenuOption::Stats, false); + // Qt::CTRL | Qt::SHIFT | Qt::Key_L + addMenuItem(VIEW_MENU, MenuOption::Log, [=] { + qApp->toggleLogDialog(); + }); + addMenuItem(VIEW_MENU, MenuOption::BandwidthDetails, [=] { + dialogsManager->bandwidthDetails(); + }); + addMenuItem(VIEW_MENU, MenuOption::OctreeStats, [=] { + dialogsManager->octreeStatsDetails(); + }); + } + + { + static const QString DEV_MENU{ "Developer" }; + addMenu(ROOT_MENU, DEV_MENU); + { + static const QString RENDER_MENU{ "Render" }; + addMenu(DEV_MENU, RENDER_MENU); + // QML Qt::SHIFT | Qt::Key_A, + addCheckableMenuItem(RENDER_MENU, MenuOption::Atmosphere, true); + addCheckableMenuItem(RENDER_MENU, MenuOption::AmbientOcclusion); + addCheckableMenuItem(RENDER_MENU, MenuOption::DontFadeOnOctreeServerChanges); + { + static const QString LIGHT_MENU{ MenuOption::RenderAmbientLight }; + addMenu(RENDER_MENU, LIGHT_MENU); + static QStringList LIGHTS{ + MenuOption::RenderAmbientLightGlobal, + MenuOption::RenderAmbientLight0, + MenuOption::RenderAmbientLight1, + MenuOption::RenderAmbientLight2, + MenuOption::RenderAmbientLight3, + MenuOption::RenderAmbientLight4, + MenuOption::RenderAmbientLight5, + MenuOption::RenderAmbientLight6, + MenuOption::RenderAmbientLight7, + MenuOption::RenderAmbientLight8, + MenuOption::RenderAmbientLight9, + }; + foreach(QString option, LIGHTS) { + addCheckableMenuItem(LIGHT_MENU, option); + // FIXME + // setExclusiveGroup() + } + setChecked(MenuOption::RenderAmbientLightGlobal, true); + } + { + static const QString SHADOWS_MENU{ "Shadows" }; + addMenu(RENDER_MENU, SHADOWS_MENU); + addCheckableMenuItem(SHADOWS_MENU, "No Shadows", true); + addCheckableMenuItem(SHADOWS_MENU, MenuOption::SimpleShadows); + addCheckableMenuItem(SHADOWS_MENU, MenuOption::CascadedShadows); + } + { + static const QString FRAMERATE_MENU{ MenuOption::RenderTargetFramerate }; + addMenu(RENDER_MENU, FRAMERATE_MENU); + //framerateGroup->setExclusive(true); + addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerateUnlimited, true); + addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate60); + addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate50); + addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate40); + addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate30); + } +#if !defined(Q_OS_MAC) + addCheckableMenuItem(RENDER_MENU, MenuOption::RenderTargetFramerateVSyncOn, true, [](bool checked) { + qApp->setVSyncEnabled(); + }); +#endif + { + static const QString RES_MENU{ MenuOption::RenderResolution }; + addMenu(RENDER_MENU, RES_MENU); + // resolutionGroup->setExclusive(true); + addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionOne, true); + addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionTwoThird); + addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionHalf); + addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionThird); + addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionQuarter); + } + // QML Qt::Key_Asterisk, + addCheckableMenuItem(RENDER_MENU, MenuOption::Stars, true); + addCheckableMenuItem(RENDER_MENU, MenuOption::EnableGlowEffect, true, [](bool checked){ + DependencyManager::get()->toggleGlowEffect(checked); + }); + //Qt::ALT | Qt::Key_W + addCheckableMenuItem(RENDER_MENU, MenuOption::Wireframe); + // QML Qt::SHIFT | Qt::Key_L, + addMenuItem(RENDER_MENU, MenuOption::LodTools, [=] { + dialogsManager->lodTools(); + }); + } + + { + static const QString AVATAR_MENU{ "Avatar Dev" }; + addMenu(DEV_MENU, AVATAR_MENU); + setText(AVATAR_MENU, "Avatar"); + { + } + } + } + +#if 0 QMenu* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); { QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu); @@ -730,362 +717,4 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } } -QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut, - const QObject* receiver, - const char* member, - QAction::MenuRole role, - int menuItemLocation) { - QAction* action = NULL; - QAction* actionBefore = NULL; - - if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { - actionBefore = destinationMenu->actions()[menuItemLocation]; - } - - if (!actionBefore) { - if (receiver && member) { - action = destinationMenu->addAction(actionName, receiver, member, shortcut); - } else { - action = destinationMenu->addAction(actionName); - action->setShortcut(shortcut); - } - } else { - action = new QAction(actionName, destinationMenu); - action->setShortcut(shortcut); - destinationMenu->insertAction(actionBefore, action); - - if (receiver && member) { - connect(action, SIGNAL(triggered()), receiver, member); - } - } - action->setMenuRole(role); - - _actionHash.insert(actionName, action); - - return action; -} - -QAction* Menu::addActionToQMenuAndActionHash(QMenu* destinationMenu, - QAction* action, - const QString& actionName, - const QKeySequence& shortcut, - QAction::MenuRole role, - int menuItemLocation) { - QAction* actionBefore = NULL; - - if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { - actionBefore = destinationMenu->actions()[menuItemLocation]; - } - - if (!actionName.isEmpty()) { - action->setText(actionName); - } - - if (shortcut != 0) { - action->setShortcut(shortcut); - } - - if (role != QAction::NoRole) { - action->setMenuRole(role); - } - - if (!actionBefore) { - destinationMenu->addAction(action); - } else { - destinationMenu->insertAction(actionBefore, action); - } - - _actionHash.insert(action->text(), action); - - return action; -} - -QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu, - const QString& actionName, - const QKeySequence& shortcut, - const bool checked, - const QObject* receiver, - const char* member, - int menuItemLocation) { - - QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, - QAction::NoRole, menuItemLocation); - action->setCheckable(true); - action->setChecked(checked); - - return action; -} - -void Menu::removeAction(QMenu* menu, const QString& actionName) { - menu->removeAction(_actionHash.value(actionName)); - _actionHash.remove(actionName); -} - #endif - -void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { -} - -bool Menu::isOptionChecked(const QString& menuOption) const { - return false; -} - -void Menu::triggerOption(const QString& menuOption) { -} - -#if 0 - -QAction* Menu::getActionForOption(const QString& menuOption) { - return _actionHash.value(menuOption); -} - -QAction* Menu::getActionFromName(const QString& menuName, QMenu* menu) { - QList menuActions; - if (menu) { - menuActions = menu->actions(); - } else { - menuActions = actions(); - } - - foreach (QAction* menuAction, menuActions) { - if (menuName == menuAction->text()) { - return menuAction; - } - } - return NULL; -} - -QMenu* Menu::getSubMenuFromName(const QString& menuName, QMenu* menu) { - QAction* action = getActionFromName(menuName, menu); - if (action) { - return action->menu(); - } - return NULL; -} - -QMenu* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { - QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; - QMenu* menu = NULL; - foreach (QString menuTreePart, menuTree) { - parent = menu; - finalMenuPart = menuTreePart.trimmed(); - menu = getSubMenuFromName(finalMenuPart, parent); - if (!menu) { - break; - } - } - return parent; -} - -QMenu* Menu::getMenu(const QString& menuName) { - QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; - QMenu* menu = NULL; - int item = 0; - foreach (QString menuTreePart, menuTree) { - menu = getSubMenuFromName(menuTreePart.trimmed(), parent); - if (!menu) { - break; - } - parent = menu; - item++; - } - return menu; -} - -QAction* Menu::getMenuAction(const QString& menuName) { - QStringList menuTree = menuName.split(">"); - QMenu* parent = NULL; - QAction* action = NULL; - foreach (QString menuTreePart, menuTree) { - action = getActionFromName(menuTreePart.trimmed(), parent); - if (!action) { - break; - } - parent = action->menu(); - } - return action; -} - -int Menu::findPositionOfMenuItem(QMenu* menu, const QString& searchMenuItem) { - int position = 0; - foreach(QAction* action, menu->actions()) { - if (action->text() == searchMenuItem) { - return position; - } - position++; - } - return UNSPECIFIED_POSITION; // not found -} - -int Menu::positionBeforeSeparatorIfNeeded(QMenu* menu, int requestedPosition) { - QList menuActions = menu->actions(); - if (requestedPosition > 1 && requestedPosition < menuActions.size()) { - QAction* beforeRequested = menuActions[requestedPosition - 1]; - if (beforeRequested->isSeparator()) { - requestedPosition--; - } - } - return requestedPosition; -} - - -QMenu* Menu::addMenu(const QString& menuName) { - QStringList menuTree = menuName.split(">"); - QMenu* addTo = NULL; - QMenu* menu = NULL; - foreach (QString menuTreePart, menuTree) { - menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); - if (!menu) { - if (!addTo) { - menu = QMenuBar::addMenu(menuTreePart.trimmed()); - } else { - menu = addTo->addMenu(menuTreePart.trimmed()); - } - } - addTo = menu; - } - - QMenuBar::repaint(); - return menu; -} - -void Menu::removeMenu(const QString& menuName) { - QAction* action = getMenuAction(menuName); - - // only proceed if the menu actually exists - if (action) { - QString finalMenuPart; - QMenu* parent = getMenuParent(menuName, finalMenuPart); - if (parent) { - parent->removeAction(action); - } else { - QMenuBar::removeAction(action); - } - - QMenuBar::repaint(); - } -} - -bool Menu::menuExists(const QString& menuName) { - QAction* action = getMenuAction(menuName); - - // only proceed if the menu actually exists - if (action) { - return true; - } - return false; -} - -void Menu::addSeparator(const QString& menuName, const QString& separatorName) { - QMenu* menuObj = getMenu(menuName); - if (menuObj) { - addDisabledActionAndSeparator(menuObj, separatorName); - } -} - -void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { - QMenu* menu = getMenu(menuName); - bool separatorRemoved = false; - if (menu) { - int textAt = findPositionOfMenuItem(menu, separatorName); - QList menuActions = menu->actions(); - QAction* separatorText = menuActions[textAt]; - if (textAt > 0 && textAt < menuActions.size()) { - QAction* separatorLine = menuActions[textAt - 1]; - if (separatorLine) { - if (separatorLine->isSeparator()) { - menu->removeAction(separatorText); - menu->removeAction(separatorLine); - separatorRemoved = true; - } - } - } - } - if (separatorRemoved) { - QMenuBar::repaint(); - } -} - -void Menu::addMenuItem(const MenuItemProperties& properties) { - QMenu* menuObj = getMenu(properties.menuName); - if (menuObj) { - QShortcut* shortcut = NULL; - if (!properties.shortcutKeySequence.isEmpty()) { - shortcut = new QShortcut(properties.shortcutKeySequence, this); - } - - // check for positioning requests - int requestedPosition = properties.position; - if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) { - requestedPosition = findPositionOfMenuItem(menuObj, properties.beforeItem); - // double check that the requested location wasn't a separator label - requestedPosition = positionBeforeSeparatorIfNeeded(menuObj, requestedPosition); - } - if (requestedPosition == UNSPECIFIED_POSITION && !properties.afterItem.isEmpty()) { - int afterPosition = findPositionOfMenuItem(menuObj, properties.afterItem); - if (afterPosition != UNSPECIFIED_POSITION) { - requestedPosition = afterPosition + 1; - } - } - - QAction* menuItemAction = NULL; - if (properties.isSeparator) { - addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition); - } else if (properties.isCheckable) { - menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, - properties.shortcutKeySequence, properties.isChecked, - MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition); - } else { - menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, - MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), - QAction::NoRole, requestedPosition); - } - if (shortcut && menuItemAction) { - connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger())); - } - QMenuBar::repaint(); - } -} -#endif - -void Menu::removeMenuItem(const QString& menuitem) { -}; - -#if 0 -bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { - QAction* menuItemAction = _actionHash.value(menuitem); - if (menuItemAction) { - return (getMenu(menu) != NULL); - } - return false; -}; - -#endif - -void Menu::setOptionText(const QString& menuitem, const QString& text) { -} - -void Menu::addMenu(const QString& parentMenu, const QString& text) { -} - -void Menu::addMenuItem(const QString& parentMenu, const QString& menuItem) { -} - -void Menu::addMenuItem(const QString& parentMenu, const QString& menuItem, std::function f) { - addMenuItem(parentMenu, menuItem); - setOptionTriggerAction(parentMenu, f); -} - -void Menu::enableMenuItem(const QString& menuItem, bool enable) { -} - -void Menu::setOptionTriggerAction(const QString& menuOption, std::function f) { - _triggerActions[menuOption] = f; -} -void Menu::setOptionToggleAction(const QString& menuOption, std::function f) { - _toggleActions[menuOption] = f; -} diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 04275fcb32..e02dd7a789 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -24,6 +24,7 @@ #include #include "DiscoverabilityManager.h" +#include class Settings; @@ -41,46 +42,58 @@ public: void setToggleAction(std::function); }; -class Menu : public QQuickItem { +class Menu : public HifiMenu { Q_OBJECT - HIFI_QML_DECL + public: Menu(QQuickItem * parent = 0); static Menu* getInstance(); - + + // Override the base type HifiMenu with this class instead + static void registerType() { + qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); + } + void loadSettings(); void saveSettings(); HifiAction * getActionForOption(const QString& menuOption) { return new HifiAction(menuOption); } // QMenu* addMenu(const QString& menuName); - void removeMenu(const QString& menuName); - bool menuExists(const QString& menuName); - void addSeparator(const QString& menuName, const QString& separatorName); - void removeSeparator(const QString& menuName, const QString& separatorName); - void addMenuItem(const MenuItemProperties& properties); - void removeMenuItem(const QString& menuitem); - bool menuItemExists(const QString& menuName, const QString& menuitem); - bool isOptionChecked(const QString& menuOption) const; - void setIsOptionChecked(const QString& menuOption, bool isChecked); - void triggerOption(const QString& menuOption); - void setOptionText(const QString& menuOption, const QString & text); - void setOptionTriggerAction(const QString& menuOption, std::function f); - void setOptionToggleAction(const QString& menuOption, std::function f); - void addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f); - void addMenuItem(const QString & parentMenu, const QString & menuOption); - void addMenu(const QString & parentMenu, const QString & menuOption); - void enableMenuItem(const QString & menuOption, bool enabled = true); + //void removeMenu(const QString& menuName); + //bool menuExists(const QString& menuName); + //void addSeparator(const QString& menuName, const QString& separatorName); + //void removeSeparator(const QString& menuName, const QString& separatorName); + //void addMenuItem(const MenuItemProperties& properties); + //void removeMenuItem(const QString& menuitem); + //bool menuItemExists(const QString& menuName, const QString& menuitem); + bool isOptionChecked(const QString& menuOption) const { + return HifiMenu::isChecked(menuOption); + } + void setIsOptionChecked(const QString& menuOption, bool isChecked) { + HifiMenu::setChecked(menuOption, isChecked); + } + void triggerOption(const QString& menuOption) { + HifiMenu::triggerMenuItem(menuOption); + } + void setOptionText(const QString& menuOption, const QString & text) { + HifiMenu::setText(menuOption, text); + } + void setOptionTriggerAction(const QString& menuOption, std::function f) { + HifiMenu::setTriggerAction(menuOption, f); + } + //void setOptionToggleAction(const QString& menuOption, std::function f); + //void addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f); + //void addMenuItem(const QString & parentMenu, const QString & menuOption); + //void addMenu(const QString & parentMenu, const QString & menuOption); + //void enableMenuItem(const QString & menuOption, bool enabled = true); private: void init(); private: static Menu* _instance; - - QHash> _triggerActions; - QHash> _toggleActions; - + friend class HifiAction; }; namespace MenuOption { diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp new file mode 100644 index 0000000000..6ca034522c --- /dev/null +++ b/libraries/ui/src/HifiMenu.cpp @@ -0,0 +1,221 @@ +#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", + 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", + 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) { + // FIXME 'add sep' +// addMenu(parentMenu, separatorName); +// setEnabled() +} + +void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) { +} + +void HifiMenu::addMenuItem(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::addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f) { + setTriggerAction(menuOption, f); + addMenuItem(parentMenu, menuOption); +} + +void HifiMenu::removeMenuItem(const QString& menuOption) { + removeMenu(menuOption); +} + +bool HifiMenu::menuItemExists(const QString& menuName, const QString& menuitem) const { + return findMenuObject(menuName); +} + +void HifiMenu::triggerMenuItem(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; + } + 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::setText(const QString& menuOption, const QString & text) { + QObject* menuItem = findMenuObject(menuOption); + if (!menuItem) { + warn(menuOption); + return; + } + menuItem->setProperty("text", QVariant::fromValue(text)); +} + +void HifiMenu::setRootMenu(QObject* rootMenu) { + _rootMenu = rootMenu; +} + +void HifiMenu::enableMenuItem(const QString & menuOption, bool enabled) { + QObject* menuItem = findMenuObject(menuOption); + if (!menuItem) { + warn(menuOption); + return; + } + menuItem->setProperty("enabled", QVariant::fromValue(enabled)); +} + +void HifiMenu::addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked) { + addMenuItem(parentMenu, menuOption); + setCheckable(menuOption); + if (checked) { + setChecked(menuOption, checked); + } +} + +void HifiMenu::addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { + setToggleAction(menuOption, f); + addCheckableMenuItem(parentMenu, menuOption, checked); +} diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h new file mode 100644 index 0000000000..4d7d860224 --- /dev/null +++ b/libraries/ui/src/HifiMenu.h @@ -0,0 +1,72 @@ +// +// MenuConstants.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_MenuContants_h +#define hifi_MenuConstants_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 addMenuItem(const QString& parentMenu, const QString& menuOption); + void addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked = false); + void addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f); + void addMenuItem(const QString& parentMenu, const QString& menuOption, std::function f); + void removeMenuItem(const QString& menuitem); + bool menuItemExists(const QString& menuName, const QString& menuitem) const; + void triggerMenuItem(const QString& menuOption); + void enableMenuItem(const QString& menuOption, bool enabled = true); + bool isChecked(const QString& menuOption) const; + void setChecked(const QString& menuOption, bool isChecked = true); + void setCheckable(const QString& menuOption, bool checkable = true); + void setExclusiveGroup(const QString& menuOption, const QString & groupName); + void setText(const QString& menuOption, const QString& text); + + 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_MenuConstants_h + + + + diff --git a/libraries/ui/src/MenuConstants.cpp b/libraries/ui/src/MenuConstants.cpp deleted file mode 100644 index eeaf7b456b..0000000000 --- a/libraries/ui/src/MenuConstants.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "MenuConstants.h" -#include - -QML_DIALOG_DEF(HifiMenu) - -static bool init = false; - -HifiMenu::HifiMenu(QQuickItem * parent) : QQuickItem(parent) { - this->setEnabled(false); - qWarning() << "Setting up connection"; - connect(this, &HifiMenu::enabledChanged, this, [=]() { - if (init) { - return; - } - init = true; - foreach(QObject * action, findChildren(QRegularExpression(".*HifiAction"))) { - connect(action, SIGNAL(triggeredByName(QString)), this, SLOT(onTriggeredByName(QString))); - connect(action, SIGNAL(toggledByName(QString)), this, SLOT(onToggledByName(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 (triggerActions.count(name)) { - if (toggleActions.count(name)) { - QObject * action = findChild(name + "HifiAction"); - bool checked = action->property("checked").toBool(); - toggleActions[name](checked); - } - } -} - -QHash> HifiMenu::triggerActions; -QHash> HifiMenu::toggleActions; - -void HifiMenu::setToggleAction(const QString & name, std::function f) { - toggleActions[name] = f; -} - -void HifiMenu::setTriggerAction(const QString & name, std::function f) { - triggerActions[name] = f; -} diff --git a/libraries/ui/src/MenuConstants.h b/libraries/ui/src/MenuConstants.h deleted file mode 100644 index e986410c5b..0000000000 --- a/libraries/ui/src/MenuConstants.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// MenuConstants.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_MenuContants_h -#define hifi_MenuConstants_h - -#include -#include -#include -#include "OffscreenQmlDialog.h" - -class HifiMenu : public QQuickItem -{ - Q_OBJECT - QML_DIALOG_DECL - - -public: - HifiMenu(QQuickItem * parent = nullptr); - static void setToggleAction(const QString & name, std::function f); - static void setTriggerAction(const QString & name, std::function f); -private slots: - void onTriggeredByName(const QString & name); - void onToggledByName(const QString & name); -private: - static QHash> triggerActions; - static QHash> toggleActions; -}; - -#endif // hifi_MenuConstants_h - - - - diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index d44a5dd282..7b84bb763f 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -15,6 +15,10 @@ #include #include "MessageDialog.h" + +Q_DECLARE_LOGGING_CATEGORY(offscreenFocus) +Q_LOGGING_CATEGORY(offscreenFocus, "hifi.offscreen.focus") + // Time between receiving a request to render the offscreen UI actually triggering // the render. Could possibly be increased depending on the framerate we expect to // achieve. @@ -93,10 +97,10 @@ void OffscreenUi::create(QOpenGLContext* shareContext) { #ifdef DEBUG connect(_quickWindow, &QQuickWindow::focusObjectChanged, [this]{ - qDebug() << "New focus item " << _quickWindow->focusObject(); + qCDebug(offscreenFocus) << "New focus item " << _quickWindow->focusObject(); }); connect(_quickWindow, &QQuickWindow::activeFocusItemChanged, [this] { - qDebug() << "New active focus item " << _quickWindow->activeFocusItem(); + qCDebug(offscreenFocus) << "New active focus item " << _quickWindow->activeFocusItem(); }); #endif diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index 33fb46b28a..b58b4e59cb 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -43,6 +43,17 @@ public: \ static void load(std::function f = [](QQmlContext*, QQuickItem*) {}); \ private: +#define HIFI_QML_DECL_LAMBDA \ +protected: \ + static const QString NAME; \ + static const QUrl QML; \ +public: \ + static void registerType(); \ + static void show(); \ + static void toggle(); \ + static void load(); \ +private: + #define HIFI_QML_DEF(x) \ const QUrl x::QML = QUrl(#x ".qml"); \ const QString x::NAME = #x; \ @@ -65,6 +76,25 @@ private: offscreenUi->load(QML, f); \ } +#define HIFI_QML_DEF_LAMBDA(x, f) \ + const QUrl x::QML = QUrl(#x ".qml"); \ + const QString x::NAME = #x; \ + \ + void x::registerType() { \ + qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \ + } \ + void x::show() { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->show(QML, NAME, f); \ + } \ + void x::toggle() { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->toggle(QML, NAME, f); \ + } \ + void x::load() { \ + auto offscreenUi = DependencyManager::get(); \ + offscreenUi->load(QML, f); \ + } class OffscreenUi : public OffscreenGlCanvas, public Dependency { Q_OBJECT diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index e84e5674c1..4189282de1 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -34,7 +34,7 @@ #include #include "MessageDialog.h" -#include "MenuConstants.h" +#include "HifiMenu.h" class RateCounter { std::vector times; @@ -181,11 +181,25 @@ public: 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->addMenuItem("File", "Quit", []{ + QApplication::quit(); + }); + menu->addCheckableMenuItem("File", "Toggle", false, [](bool toggled) { + qDebug() << "Toggle is " << toggled; + }); + menu->addMenu("", "Edit"); + menu->addMenuItem("Edit", "Undo"); + menu->addMenuItem("Edit", "Redo"); + menu->addMenuItem("Edit", "Copy"); + menu->addMenuItem("Edit", "Cut"); + menu->addMenuItem("Edit", "Paste"); + menu->addMenu("", "Long Menu Name..."); #endif installEventFilter(offscreenUi.data()); - //HifiMenu::setTriggerAction(MenuOption::Quit, [] { - // QApplication::quit(); - //}); offscreenUi->resume(); _timer.start(); } @@ -233,30 +247,16 @@ protected: resizeWindow(ev->size()); } - - static QObject * addMenu(QObject * parent, const QString & text) { - // FIXME add more checking here to ensure no name conflicts - QVariant returnedValue; - QMetaObject::invokeMethod(parent, "addMenu", - Q_RETURN_ARG(QVariant, returnedValue), - Q_ARG(QVariant, text)); - QObject * result = returnedValue.value(); - return result; - } - + 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(); - rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); - QObject * result = addMenu(rootMenu, "Test 3"); - result->setParent(rootMenu); - qDebug() << "Added " << result; - if (menuContext) { - menuContext->setContextProperty("rootMenu", rootMenu); - } + HifiMenu * menu = offscreenUi->findChild(); + menu->addMenuItem("", "Test 3"); + menu->addMenuItem("File", "Test 3"); } break; case Qt::Key_K: @@ -279,12 +279,7 @@ protected: QQmlContext* menuContext{ nullptr }; void keyReleaseEvent(QKeyEvent *event) { if (_altPressed && Qt::Key_Alt == event->key()) { - HifiMenu::toggle([=](QQmlContext* context, QObject* newItem) { - auto offscreenUi = DependencyManager::get(); - rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); - menuContext = context; - menuContext->setContextProperty("rootMenu", rootMenu); - }); + HifiMenu::toggle(); } } @@ -327,13 +322,12 @@ void QTestWindow::renderQml() { const char * LOG_FILTER_RULES = R"V0G0N( *.debug=false -qt.quick.dialogs.registration=true -qt.quick.mouse.debug = true +qt.quick.mouse.debug=false )V0G0N"; 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; From d68d13489daa4310d8d1b5070d9856ae2a5f4171 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Apr 2015 21:55:31 -0700 Subject: [PATCH 09/33] Working on appearance of menus --- interface/resources/qml/HifiMenu.qml | 50 ++++++++++++------- .../resources/qml/controls/FontAwesome.qml | 16 ++++++ 2 files changed, 47 insertions(+), 19 deletions(-) create mode 100644 interface/resources/qml/controls/FontAwesome.qml diff --git a/interface/resources/qml/HifiMenu.qml b/interface/resources/qml/HifiMenu.qml index 8bdda71558..254ae32520 100644 --- a/interface/resources/qml/HifiMenu.qml +++ b/interface/resources/qml/HifiMenu.qml @@ -47,8 +47,8 @@ Hifi.HifiMenu { property var itemBuilder: Component { Text { SystemPalette { id: sp; colorGroup: SystemPalette.Active } - id: thisText + x: 32 property var source property var root property var listViewIndex @@ -59,17 +59,25 @@ Hifi.HifiMenu { color: source.enabled ? "black" : "gray" onImplicitWidthChanged: { - width = implicitWidth if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth); + listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); listView.recalculateSize(); } } - - onImplicitHeightChanged: { - height = implicitHeight + + 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: @@ -84,7 +92,11 @@ Hifi.HifiMenu { MouseArea { id: mouseArea acceptedButtons: Qt.LeftButton - anchors.fill: parent + 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); @@ -114,7 +126,7 @@ Hifi.HifiMenu { onCountChanged: { recalculateSize() - } + } function recalculateSize() { var newHeight = 0 @@ -128,7 +140,7 @@ Hifi.HifiMenu { } highlight: Rectangle { - width: 0; height: 32 + width: listView.minWidth; height: 32 color: sysPalette.highlight y: (listView.currentItem) ? listView.currentItem.y : 0; Behavior on y { @@ -138,15 +150,7 @@ Hifi.HifiMenu { } } } - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Escape: - console.log("Backing out") - root.popColumn() - event.accepted = true; - } - } - + property int columnIndex: root.models.length - 1 model: root.models[columnIndex] @@ -364,12 +368,20 @@ Hifi.HifiMenu { } */ + 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(mouseArea.pressedButtons & Qt.RightButton) { + if (mouse.button == Qt.RightButton) { root.popColumn(); } else { root.enabled = false; diff --git a/interface/resources/qml/controls/FontAwesome.qml b/interface/resources/qml/controls/FontAwesome.qml new file mode 100644 index 0000000000..16fd4a6ecc --- /dev/null +++ b/interface/resources/qml/controls/FontAwesome.qml @@ -0,0 +1,16 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.3 +import QtQuick.Controls.Styles 1.3 + +Text { + id: root + FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; } + property int size: 32 + width: size + height: size + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + font.family: iconFont.name + font.pointSize: 18 +} + From 1d316a187294788182206acc620b5486779d092b Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 23 Apr 2015 23:41:09 -0700 Subject: [PATCH 10/33] Still working on menus --- interface/src/Bookmarks.cpp | 12 +- interface/src/Menu.cpp | 535 ++++++++++++++++------------------ interface/src/Menu.h | 27 +- libraries/ui/src/HifiMenu.cpp | 60 +++- libraries/ui/src/HifiMenu.h | 27 +- 5 files changed, 334 insertions(+), 327 deletions(-) diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 5af8234c7f..76ab607ff5 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -87,11 +87,11 @@ void Bookmarks::persistToFile() { void Bookmarks::setupMenus(const QString & parentMenu) { // Add menus/actions Menu * menu = Menu::getInstance(); - menu->addMenuItem(parentMenu, MenuOption::BookmarkLocation, [this] { + menu->addItem(parentMenu, MenuOption::BookmarkLocation, [this] { bookmarkLocation(); }); menu->addMenu(parentMenu, MenuOption::Bookmarks); - menu->addMenuItem(parentMenu, MenuOption::DeleteBookmark, [this] { + menu->addItem(parentMenu, MenuOption::DeleteBookmark, [this] { deleteBookmark(); }); @@ -178,19 +178,19 @@ void Bookmarks::deleteBookmark() { void Bookmarks::enableMenuItems(bool enabled) { Menu* menu = Menu::getInstance(); - menu->enableMenuItem(MenuOption::Bookmarks, enabled); - menu->enableMenuItem(MenuOption::DeleteBookmark, enabled); + menu->enableItem(MenuOption::Bookmarks, enabled); + menu->enableItem(MenuOption::DeleteBookmark, enabled); } void Bookmarks::addLocationToMenu(QString& name, QString& address) { - Menu::getInstance()->addMenuItem(MenuOption::Bookmarks, name, [=] { + Menu::getInstance()->addItem(MenuOption::Bookmarks, name, [=] { DependencyManager::get()->handleLookupString(address); }); } void Bookmarks::removeLocationFromMenu(QString& name) { - Menu::getInstance()->removeMenuItem(name); + Menu::getInstance()->removeItem(name); } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 529fca5e66..029c150884 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -42,6 +42,7 @@ #include "InterfaceLogging.h" #include "Menu.h" +#include "Util.h" // Proxy object to simplify porting over HifiAction::HifiAction(const QString & menuOption) : _menuOption(menuOption) { @@ -57,28 +58,15 @@ void HifiAction::setChecked(bool checked) { } void HifiAction::setVisible(bool visible) { - QObject* result = Menu::getInstance()->findMenuObject(_menuOption); - if (result) { - result->setProperty("visible", visible); - } + return Menu::getInstance()->setItemVisible(_menuOption); } -const QString & HifiAction::shortcut() const { - static const QString NONE; - QObject* result = Menu::getInstance()->findMenuObject(_menuOption); - if (!result) { - return NONE; - } - QObject* shortcut = result->property("shortcut").value(); - if (!shortcut) { - return NONE; - } - shortcut->dumpObjectInfo(); - return NONE; +QString HifiAction::shortcut() const { + return Menu::getInstance()->getItemShortcut(_menuOption); } void HifiAction::setText(const QString & text) { - Menu::getInstance()->setText(_menuOption, text); + Menu::getInstance()->setItemText(_menuOption, text); } void HifiAction::setTriggerAction(std::function f) { @@ -116,7 +104,6 @@ Menu* Menu::getInstance() { Menu::Menu(QQuickItem * parent) : HifiMenu(parent) { } - void Menu::init() { auto dialogsManager = DependencyManager::get(); AccountManager& accountManager = AccountManager::getInstance(); @@ -125,7 +112,7 @@ void Menu::init() { static const QString FILE_MENU{ "File" }; addMenu(ROOT_MENU, FILE_MENU); { - addMenuItem(FILE_MENU, MenuOption::Login); + addItem(FILE_MENU, MenuOption::Login); // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item connect(&accountManager, &AccountManager::profileChanged, dialogsManager.data(), &DialogsManager::toggleLoginDialog); @@ -141,22 +128,22 @@ void Menu::init() { static const QString SCRIPTS_MENU{ "Scripts" }; addMenu(FILE_MENU, SCRIPTS_MENU); //Qt::CTRL | Qt::Key_O - addMenuItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { + addItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { qApp->loadDialog(); }); //Qt::CTRL | Qt::SHIFT | Qt::Key_O - addMenuItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { + addItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { qApp->loadScriptURLDialog(); }); - addMenuItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { + addItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { qApp->stopAllScripts(); }); //Qt::CTRL | Qt::Key_R, - addMenuItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { + addItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { qApp->reloadAllScripts(); }); // Qt::CTRL | Qt::Key_J, - addMenuItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { + addItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { qApp->toggleRunningScriptsWidget(); }); } @@ -166,15 +153,15 @@ void Menu::init() { addMenu(FILE_MENU, LOCATION_MENU); qApp->getBookmarks()->setupMenus(LOCATION_MENU); //Qt::CTRL | Qt::Key_L - addMenuItem(LOCATION_MENU, MenuOption::AddressBar, [=] { + addItem(LOCATION_MENU, MenuOption::AddressBar, [=] { auto dialogsManager = DependencyManager::get(); dialogsManager->toggleAddressBar(); }); - addMenuItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { + addItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { auto addressManager = DependencyManager::get(); addressManager->copyAddress(); }); - addMenuItem(LOCATION_MENU, MenuOption::CopyPath, [=] { + addItem(LOCATION_MENU, MenuOption::CopyPath, [=] { auto addressManager = DependencyManager::get(); addressManager->copyPath(); }); @@ -182,7 +169,7 @@ void Menu::init() { // Qt::CTRL | Qt::Key_Q // QAction::QuitRole - addMenuItem(FILE_MENU, MenuOption::Quit, [=] { + addItem(FILE_MENU, MenuOption::Quit, [=] { qApp->quit(); }); } @@ -204,10 +191,10 @@ void Menu::init() { // Qt::CTRL | Qt::Key_Comma // QAction::PreferencesRole - addMenuItem(EDIT_MENU, MenuOption::Preferences, [=] { + addItem(EDIT_MENU, MenuOption::Preferences, [=] { dialogsManager->editPreferences(); }); - addMenuItem(EDIT_MENU, MenuOption::Animations, [=] { + addItem(EDIT_MENU, MenuOption::Animations, [=] { dialogsManager->editAnimations(); }); } @@ -219,65 +206,60 @@ void Menu::init() { #if defined(Q_OS_MAC) || defined(Q_OS_WIN) auto speechRecognizer = DependencyManager::get(); //Qt::CTRL | Qt::SHIFT | Qt::Key_C - addMenuItem(TOOLS_MENU, MenuOption::ControlWithSpeech); + addItem(TOOLS_MENU, MenuOption::ControlWithSpeech); setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); connect(speechRecognizer.data(), &SpeechRecognizer::enabledUpdated, [=] { setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); }); #endif // Qt::ALT | Qt::Key_S, - addMenuItem(TOOLS_MENU, MenuOption::ScriptEditor, [=] { + addItem(TOOLS_MENU, MenuOption::ScriptEditor, [=] { dialogsManager->showScriptEditor(); }); // QML Qt::Key_Backslash, - addMenuItem(TOOLS_MENU, MenuOption::Chat, [=] { + addItem(TOOLS_MENU, MenuOption::Chat, [=] { dialogsManager->showIRCLink(); }); - addMenuItem(TOOLS_MENU, MenuOption::AddRemoveFriends, [=] { + addItem(TOOLS_MENU, MenuOption::AddRemoveFriends, [=] { qApp->showFriendsWindow(); }); -#if 0 - QMenu* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); { - QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); + static const QString VIZ_MENU{ "I Am Visible To" }; + addMenu(TOOLS_MENU, VIZ_MENU); auto discoverabilityManager = DependencyManager::get(); - - QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToEveryone); - - QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToFriends); - - QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne, - 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, - discoverabilityManager.data(), SLOT(setVisibility())); - visibilityGroup->addAction(visibleToNoOne); + // FIXME group + addCheckableItem(VIZ_MENU, MenuOption::VisibleToEveryone, + discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, + [=](bool) { discoverabilityManager->setVisibility(); }); + addCheckableItem(VIZ_MENU, MenuOption::VisibleToFriends, + discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, + [=](bool) { discoverabilityManager->setVisibility(); }); + addCheckableItem(VIZ_MENU, MenuOption::VisibleToNoOne, + discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, + [=](bool) { discoverabilityManager->setVisibility(); }); connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); + } -#endif + //Qt::CTRL | Qt::ALT | Qt::Key_T, - addMenuItem(TOOLS_MENU, MenuOption::ToolWindow, [=] { + addItem(TOOLS_MENU, MenuOption::ToolWindow, [=] { // dialogsManager->toggleToolWindow(); }); //Qt::CTRL | Qt::ALT | Qt::Key_J, - addMenuItem(TOOLS_MENU, MenuOption::Console, [=] { + addItem(TOOLS_MENU, MenuOption::Console, [=] { DependencyManager::get()->toggleConsole(); }); // QML Qt::Key_Apostrophe, - addMenuItem(TOOLS_MENU, MenuOption::ResetSensors, [=] { + addItem(TOOLS_MENU, MenuOption::ResetSensors, [=] { qApp->resetSensors(); }); - addMenuItem(TOOLS_MENU, MenuOption::PackageModel, [=] { + addItem(TOOLS_MENU, MenuOption::PackageModel, [=] { qApp->packageModel(); }); } @@ -290,35 +272,35 @@ void Menu::init() { static const QString SIZE_MENU{ "Size" }; addMenu(AVATAR_MENU, SIZE_MENU); // QML Qt::Key_Plus, - addMenuItem(SIZE_MENU, MenuOption::IncreaseAvatarSize, [=] { + addItem(SIZE_MENU, MenuOption::IncreaseAvatarSize, [=] { avatar->increaseSize(); }); // QML Qt::Key_Minus, - addMenuItem(SIZE_MENU, MenuOption::DecreaseAvatarSize, [=] { + addItem(SIZE_MENU, MenuOption::DecreaseAvatarSize, [=] { avatar->decreaseSize(); }); // QML Qt::Key_Equal, - addMenuItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { + addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { avatar->resetSize(); }); - addMenuItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { + addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { avatar->resetSize(); }); } //Qt::CTRL | Qt::SHIFT | Qt::Key_K - addCheckableMenuItem(AVATAR_MENU, MenuOption::KeyboardMotorControl, true, [=](bool) { + addCheckableItem(AVATAR_MENU, MenuOption::KeyboardMotorControl, true, [=](bool) { avatar->updateMotionBehavior(); }); - addCheckableMenuItem(AVATAR_MENU, MenuOption::ScriptedMotorControl, true); - addCheckableMenuItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); - addCheckableMenuItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); - addCheckableMenuItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); - addCheckableMenuItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, [=](bool) { + addCheckableItem(AVATAR_MENU, MenuOption::ScriptedMotorControl, true); + addCheckableItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); + addCheckableItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); + addCheckableItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); + addCheckableItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, [=](bool) { avatar->updateMotionBehavior(); }); - addCheckableMenuItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, [=](bool) { + addCheckableItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, [=](bool) { avatar->updateMotionBehavior(); }); } @@ -329,57 +311,58 @@ void Menu::init() { // Mac Qt::CTRL | Qt::META | Qt::Key_F, // Win32/Linux Qt::CTRL | Qt::Key_F, - addCheckableMenuItem(VIEW_MENU, MenuOption::Fullscreen, false, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::Fullscreen, false, [=](bool checked) { // qApp->setFullscreen(checked); }); // QML Qt::Key_P, - addCheckableMenuItem(VIEW_MENU, MenuOption::FirstPerson, true, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::FirstPerson, true, [=](bool checked) { // qApp->cameraMenuChanged(); }); //QML Qt::SHIFT | Qt::Key_H, - addCheckableMenuItem(VIEW_MENU, MenuOption::Mirror, true); + addCheckableItem(VIEW_MENU, MenuOption::Mirror, true); // QML Qt::Key_H, - addCheckableMenuItem(VIEW_MENU, MenuOption::FullscreenMirror, true, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::FullscreenMirror, true, [=](bool checked) { // qApp->cameraMenuChanged(); }); // Mac Qt::META | Qt::Key_H, // Win32/Linux Qt::CTRL | Qt::Key_H, - addCheckableMenuItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { dialogsManager->hmdTools(checked); }); - addCheckableMenuItem(VIEW_MENU, MenuOption::EnableVRMode, false, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::EnableVRMode, false, [=](bool checked) { // qApp->setEnableVRMode(checked); }); - addCheckableMenuItem(VIEW_MENU, MenuOption::Enable3DTVMode, false, [=](bool checked) { + addCheckableItem(VIEW_MENU, MenuOption::Enable3DTVMode, false, [=](bool checked) { // qApp->setEnable3DTVMode(checked); }); { - static const QString BORDER_MENU{ "View" }; + static const QString BORDER_MENU{ "Server Borders" }; addMenu(VIEW_MENU, BORDER_MENU); // Qt::CTRL | Qt::SHIFT | Qt::Key_1 - addCheckableMenuItem(BORDER_MENU, MenuOption::ShowBordersEntityNodes, false, [=](bool checked) { + addCheckableItem(BORDER_MENU, MenuOption::ShowBordersEntityNodes, false, [=](bool checked) { qApp->getNodeBoundsDisplay().setShowEntityNodes(checked); }); } - addCheckableMenuItem(VIEW_MENU, MenuOption::OffAxisProjection, false); - addCheckableMenuItem(VIEW_MENU, MenuOption::TurnWithHead, false); + addCheckableItem(VIEW_MENU, MenuOption::OffAxisProjection, false); + addCheckableItem(VIEW_MENU, MenuOption::TurnWithHead, false); // QML Qt::Key_Slash - addCheckableMenuItem(VIEW_MENU, MenuOption::Stats, false); + addCheckableItem(VIEW_MENU, MenuOption::Stats, false); // Qt::CTRL | Qt::SHIFT | Qt::Key_L - addMenuItem(VIEW_MENU, MenuOption::Log, [=] { + addItem(VIEW_MENU, MenuOption::Log, [=] { qApp->toggleLogDialog(); }); - addMenuItem(VIEW_MENU, MenuOption::BandwidthDetails, [=] { + addItem(VIEW_MENU, MenuOption::BandwidthDetails, [=] { dialogsManager->bandwidthDetails(); }); - addMenuItem(VIEW_MENU, MenuOption::OctreeStats, [=] { + addItem(VIEW_MENU, MenuOption::OctreeStats, [=] { dialogsManager->octreeStatsDetails(); }); } + { static const QString DEV_MENU{ "Developer" }; @@ -388,9 +371,9 @@ void Menu::init() { static const QString RENDER_MENU{ "Render" }; addMenu(DEV_MENU, RENDER_MENU); // QML Qt::SHIFT | Qt::Key_A, - addCheckableMenuItem(RENDER_MENU, MenuOption::Atmosphere, true); - addCheckableMenuItem(RENDER_MENU, MenuOption::AmbientOcclusion); - addCheckableMenuItem(RENDER_MENU, MenuOption::DontFadeOnOctreeServerChanges); + addCheckableItem(RENDER_MENU, MenuOption::Atmosphere, true); + addCheckableItem(RENDER_MENU, MenuOption::AmbientOcclusion); + addCheckableItem(RENDER_MENU, MenuOption::DontFadeOnOctreeServerChanges); { static const QString LIGHT_MENU{ MenuOption::RenderAmbientLight }; addMenu(RENDER_MENU, LIGHT_MENU); @@ -408,7 +391,7 @@ void Menu::init() { MenuOption::RenderAmbientLight9, }; foreach(QString option, LIGHTS) { - addCheckableMenuItem(LIGHT_MENU, option); + addCheckableItem(LIGHT_MENU, option); // FIXME // setExclusiveGroup() } @@ -417,22 +400,22 @@ void Menu::init() { { static const QString SHADOWS_MENU{ "Shadows" }; addMenu(RENDER_MENU, SHADOWS_MENU); - addCheckableMenuItem(SHADOWS_MENU, "No Shadows", true); - addCheckableMenuItem(SHADOWS_MENU, MenuOption::SimpleShadows); - addCheckableMenuItem(SHADOWS_MENU, MenuOption::CascadedShadows); + addCheckableItem(SHADOWS_MENU, "No Shadows", true); + addCheckableItem(SHADOWS_MENU, MenuOption::SimpleShadows); + addCheckableItem(SHADOWS_MENU, MenuOption::CascadedShadows); } { static const QString FRAMERATE_MENU{ MenuOption::RenderTargetFramerate }; addMenu(RENDER_MENU, FRAMERATE_MENU); //framerateGroup->setExclusive(true); - addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerateUnlimited, true); - addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate60); - addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate50); - addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate40); - addCheckableMenuItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate30); + addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerateUnlimited, true); + addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate60); + addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate50); + addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate40); + addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate30); } #if !defined(Q_OS_MAC) - addCheckableMenuItem(RENDER_MENU, MenuOption::RenderTargetFramerateVSyncOn, true, [](bool checked) { + addCheckableItem(RENDER_MENU, MenuOption::RenderTargetFramerateVSyncOn, true, [](bool checked) { qApp->setVSyncEnabled(); }); #endif @@ -440,223 +423,211 @@ void Menu::init() { static const QString RES_MENU{ MenuOption::RenderResolution }; addMenu(RENDER_MENU, RES_MENU); // resolutionGroup->setExclusive(true); - addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionOne, true); - addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionTwoThird); - addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionHalf); - addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionThird); - addCheckableMenuItem(RES_MENU, MenuOption::RenderResolutionQuarter); + addCheckableItem(RES_MENU, MenuOption::RenderResolutionOne, true); + addCheckableItem(RES_MENU, MenuOption::RenderResolutionTwoThird); + addCheckableItem(RES_MENU, MenuOption::RenderResolutionHalf); + addCheckableItem(RES_MENU, MenuOption::RenderResolutionThird); + addCheckableItem(RES_MENU, MenuOption::RenderResolutionQuarter); } // QML Qt::Key_Asterisk, - addCheckableMenuItem(RENDER_MENU, MenuOption::Stars, true); - addCheckableMenuItem(RENDER_MENU, MenuOption::EnableGlowEffect, true, [](bool checked){ + addCheckableItem(RENDER_MENU, MenuOption::Stars, true); + addCheckableItem(RENDER_MENU, MenuOption::EnableGlowEffect, true, [](bool checked){ DependencyManager::get()->toggleGlowEffect(checked); }); //Qt::ALT | Qt::Key_W - addCheckableMenuItem(RENDER_MENU, MenuOption::Wireframe); + addCheckableItem(RENDER_MENU, MenuOption::Wireframe); // QML Qt::SHIFT | Qt::Key_L, - addMenuItem(RENDER_MENU, MenuOption::LodTools, [=] { + addItem(RENDER_MENU, MenuOption::LodTools, [=] { dialogsManager->lodTools(); }); - } + } // Developer -> Render { static const QString AVATAR_MENU{ "Avatar Dev" }; addMenu(DEV_MENU, AVATAR_MENU); - setText(AVATAR_MENU, "Avatar"); + setItemText(AVATAR_MENU, "Avatar"); { - } - } - } - -#if 0 - QMenu* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); - { - QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu); - - QAction* noFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::NoFaceTracking, - 0, true, - qApp, SLOT(setActiveFaceTracker())); - faceTrackerGroup->addAction(noFaceTracker); - + static const QString FACE_MENU{ "Face Tracking" }; + addMenu(AVATAR_MENU, FACE_MENU); + // FIXME GROUP + addCheckableItem(FACE_MENU, MenuOption::NoFaceTracking, true, [](bool checked) { + qApp->setActiveFaceTracker(); + }); #ifdef HAVE_FACESHIFT - QAction* faceshiftFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Faceshift, - 0, false, - qApp, SLOT(setActiveFaceTracker())); - faceTrackerGroup->addAction(faceshiftFaceTracker); + addCheckableItem(FACE_MENU, MenuOption::Faceshift, true, [](bool checked) { + qApp->setActiveFaceTracker(); + }); #endif #ifdef HAVE_DDE - QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, - 0, false, - qApp, SLOT(setActiveFaceTracker())); - faceTrackerGroup->addAction(ddeFaceTracker); -#endif - } -#ifdef HAVE_DDE - faceTrackingMenu->addSeparator(); - QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true); - useAudioForMouth->setVisible(false); - QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true); - ddeFiltering->setVisible(false); + addCheckableItem(FACE_MENU, MenuOption::UseCamera, true, [](bool checked) { + qApp->setActiveFaceTracker(); + }); #endif + addCheckableItem(FACE_MENU, MenuOption::UseAudioForMouth, true); + // FIXME integrate the visibility into the main API + getActionForOption(MenuOption::UseAudioForMouth)->setVisible(false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); - addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); - - QMenu* 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"); + addCheckableItem(FACE_MENU, MenuOption::VelocityFilter, true); + // FIXME integrate the visibility into the main API + getActionForOption(MenuOption::VelocityFilter)->setVisible(false); + } + addCheckableItem(AVATAR_MENU, MenuOption::RenderSkeletonCollisionShapes); + addCheckableItem(AVATAR_MENU, MenuOption::RenderHeadCollisionShapes); + addCheckableItem(AVATAR_MENU, MenuOption::RenderBoundingCollisionShapes); + addCheckableItem(AVATAR_MENU, MenuOption::RenderLookAtVectors); + addCheckableItem(AVATAR_MENU, MenuOption::RenderFocusIndicator); + { + static const QString HANDS_MENU{ "Hands" }; + addMenu(AVATAR_MENU, HANDS_MENU); + addCheckableItem(HANDS_MENU, MenuOption::AlignForearmsWithWrists); + addCheckableItem(HANDS_MENU, MenuOption::AlternateIK); + addCheckableItem(HANDS_MENU, MenuOption::DisplayHands, true); + addCheckableItem(HANDS_MENU, MenuOption::DisplayHandTargets); + addCheckableItem(HANDS_MENU, MenuOption::ShowIKConstraints); + { + static const QString SIXENSE_MENU{ "Sixense" }; + addMenu(HANDS_MENU, SIXENSE_MENU); #ifdef __APPLE__ - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, - MenuOption::SixenseEnabled, - 0, false, - &SixenseManager::getInstance(), - SLOT(toggleSixense(bool))); + addCheckableItem(SIXENSE_MENU, MenuOption::SixenseEnabled, false, [](bool checked) { + SixenseManager::getInstance().toggleSixense(checked); + }); #endif - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, - MenuOption::FilterSixense, - 0, - true, - &SixenseManager::getInstance(), - SLOT(setFilter(bool))); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, - MenuOption::LowVelocityFilter, - 0, - true, - qApp, - SLOT(setLowVelocityFilter(bool))); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); - addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); + addCheckableItem(SIXENSE_MENU, MenuOption::FilterSixense, true, [](bool checked) { + SixenseManager::getInstance().setFilter(checked); + }); + addCheckableItem(SIXENSE_MENU, MenuOption::LowVelocityFilter, true, [](bool checked) { + qApp->setLowVelocityFilter(checked); + }); + addCheckableItem(SIXENSE_MENU, MenuOption::SixenseMouseInput, true); + addCheckableItem(SIXENSE_MENU, MenuOption::SixenseLasers); + } - QMenu* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); - addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); + { + static const QString LEAP_MENU{ "Leap Motion" }; + addMenu(HANDS_MENU, LEAP_MENU); + addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); + addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); + } #ifdef HAVE_RSSDK - QMenu* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense"); - addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0, - RealSense::getInstance(), SLOT(loadRSSDKFile())); + { + static const QString RSS_MENU{ "RealSense" }; + addMenu(HANDS_MENU, RSS_MENU); + addItem(RSS_MENU, MenuOption::LoadRSSDKFile, [] { + RealSense::getInstance()->loadRSSDKFile(); + }); + addCheckableItem(RSS_MENU, MenuOption::LeapMotionOnHMD); + } #endif + } // Developer -> Hands - QMenu* networkMenu = developerMenu->addMenu("Network"); - addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); - addCheckableActionToQMenuAndActionHash(networkMenu, - MenuOption::DisableActivityLogger, - 0, - false, - &UserActivityLogger::getInstance(), - SLOT(disable(bool))); - addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, - dialogsManager.data(), SLOT(cachesSizeDialog())); - addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, - dialogsManager.data(), SLOT(toggleDiskCacheEditor())); + { + static const QString NETWORK_MENU{ "Network" }; + addMenu(DEV_MENU, NETWORK_MENU); + addCheckableItem(NETWORK_MENU, MenuOption::DisableNackPackets); + addCheckableItem(NETWORK_MENU, MenuOption::DisableActivityLogger, false, [](bool checked) { + UserActivityLogger::getInstance().disable(checked); + }); + addItem(NETWORK_MENU, MenuOption::CachesSize, [=] { + dialogsManager->cachesSizeDialog(); + }); + addItem(NETWORK_MENU, MenuOption::DiskCacheEditor, [=] { + dialogsManager->toggleDiskCacheEditor(); + }); + } // Developer -> Network - QMenu* timingMenu = developerMenu->addMenu("Timing and Stats"); - QMenu* perfTimerMenu = timingMenu->addMenu("Performance Timer"); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::DisplayDebugTimingDetails, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::OnlyDisplayTopTen, 0, true); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandUpdateTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); - addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); + { + static const QString TIMING_MENU{ "Timing and Stats" }; + addMenu(DEV_MENU, TIMING_MENU); + { + static const QString PERF_MENU{ "Performance Timer" }; + addMenu(TIMING_MENU, PERF_MENU); + addCheckableItem(PERF_MENU, MenuOption::DisplayDebugTimingDetails); + addCheckableItem(PERF_MENU, MenuOption::OnlyDisplayTopTen, true); + addCheckableItem(PERF_MENU, MenuOption::ExpandUpdateTiming); + addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarTiming); + addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarSimulateTiming); + addCheckableItem(PERF_MENU, MenuOption::ExpandOtherAvatarTiming); + addCheckableItem(PERF_MENU, MenuOption::ExpandPaintGLTiming); + } + addCheckableItem(TIMING_MENU, MenuOption::TestPing, true); + addCheckableItem(TIMING_MENU, MenuOption::FrameTimer); + addItem(TIMING_MENU, MenuOption::RunTimingTests, [] { runTimingTests(); }); + addCheckableItem(TIMING_MENU, MenuOption::PipelineWarnings); + addCheckableItem(TIMING_MENU, MenuOption::SuppressShortTimings); + } // Developer -> Timing and Stats - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); - addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); - addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); + { + static const QString AUDIO_MENU{ "Audio" }; + addMenu(DEV_MENU, AUDIO_MENU); + auto audioIO = DependencyManager::get(); + addCheckableItem(AUDIO_MENU, MenuOption::AudioNoiseReduction, true, [=](bool checked) { + audioIO->toggleAudioNoiseReduction(); + }); + addCheckableItem(AUDIO_MENU, MenuOption::EchoServerAudio, false, [=](bool checked) { + audioIO->toggleServerEcho(); + }); + addCheckableItem(AUDIO_MENU, MenuOption::EchoLocalAudio, false, [=](bool checked) { + audioIO->toggleLocalEcho(); + }); + addCheckableItem(AUDIO_MENU, MenuOption::StereoAudio, false, [=](bool checked) { + audioIO->toggleStereoInput(); + }); + // Qt::CTRL | Qt::Key_M, + addCheckableItem(AUDIO_MENU, MenuOption::MuteAudio, false, [=](bool checked) { + audioIO->toggleMute(); + }); + addCheckableItem(AUDIO_MENU, MenuOption::MuteEnvironment, false, [=](bool checked) { + audioIO->sendMuteEnvironmentPacket(); + }); + { + static const QString SCOPE_MENU{ "Audio Scope" }; + addMenu(AUDIO_MENU, SCOPE_MENU); + auto scope = DependencyManager::get(); + // Qt::CTRL | Qt::Key_P + addCheckableItem(SCOPE_MENU, MenuOption::AudioScope, false, [=](bool checked) { + scope->toggle(); + }); + // Qt::CTRL | Qt::SHIFT | Qt::Key_P + addCheckableItem(SCOPE_MENU, MenuOption::AudioScopePause, false, [=](bool checked) { + scope->togglePause(); + }); + addSeparator(SCOPE_MENU, "Display Frames"); - auto audioIO = DependencyManager::get(); - QMenu* audioDebugMenu = developerMenu->addMenu("Audio"); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, - 0, - true, - audioIO.data(), - SLOT(toggleAudioNoiseReduction())); + // FIXME GROUP + addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiveFrames, true, [=](bool checked) { + scope->selectAudioScopeFiveFrames(); + }); + addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeTwentyFrames, false, [=](bool checked) { + scope->selectAudioScopeTwentyFrames(); + }); + addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiftyFrames, false, [=](bool checked) { + scope->selectAudioScopeFiftyFrames(); + }); + } + auto statsRenderer = DependencyManager::get(); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, - 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, - audioIO.data(), - SLOT(toggleMute())); - addActionToQMenuAndActionHash(audioDebugMenu, - MenuOption::MuteEnvironment, - 0, - audioIO.data(), - SLOT(sendMuteEnvironmentPacket())); - - auto scope = DependencyManager::get(); + // Qt::CTRL | Qt::SHIFT | Qt::Key_A, + addCheckableItem(AUDIO_MENU, MenuOption::AudioStats, false, [=](bool checked) { + statsRenderer->toggle(); + }); + addCheckableItem(AUDIO_MENU, MenuOption::AudioStatsShowInjectedStreams, false, [=](bool checked) { + statsRenderer->toggleShowInjectedStreams(); + }); + } // Developer -> Audio + } // Developer - QMenu* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); - addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, - Qt::CTRL | Qt::Key_P, false, - scope.data(), - SLOT(toggle())); - addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, - Qt::CTRL | Qt::SHIFT | Qt::Key_P , - false, - scope.data(), - SLOT(togglePause())); - addDisabledActionAndSeparator(audioScopeMenu, "Display Frames"); - { - QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames, - 0, - true, - scope.data(), - SLOT(selectAudioScopeFiveFrames())); - - QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames, - 0, - false, - scope.data(), - SLOT(selectAudioScopeTwentyFrames())); - - QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames, - 0, - false, - scope.data(), - SLOT(selectAudioScopeFiftyFrames())); - - QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu); - audioScopeFramesGroup->addAction(fiveFrames); - audioScopeFramesGroup->addAction(twentyFrames); - audioScopeFramesGroup->addAction(fiftyFrames); + { + static const QString HELP_MENU{ "Help" }; + addMenu(ROOT_MENU, HELP_MENU); + addItem(HELP_MENU, MenuOption::EditEntitiesHelp, [] { + qApp->showEditEntitiesHelp(); + }); + addItem(HELP_MENU, MenuOption::AboutApp, [] { + qApp->aboutApp(); + }); + } // Help } - - auto statsRenderer = DependencyManager::get(); - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats, - Qt::CTRL | Qt::SHIFT | Qt::Key_A, - false, - statsRenderer.data(), - SLOT(toggle())); - - addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStatsShowInjectedStreams, - 0, - false, - statsRenderer.data(), - SLOT(toggleShowInjectedStreams())); - - QMenu* helpMenu = addMenu("Help"); - addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); - -#ifndef Q_OS_MAC - QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp); - connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); -#endif -#endif } void Menu::loadSettings() { diff --git a/interface/src/Menu.h b/interface/src/Menu.h index e02dd7a789..d23d7658d0 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -36,7 +36,7 @@ public: void setCheckable(bool); void setChecked(bool); void setVisible(bool); - const QString & shortcut() const; + QString shortcut() const; void setText(const QString &); void setTriggerAction(std::function); void setToggleAction(std::function); @@ -56,37 +56,32 @@ public: void loadSettings(); void saveSettings(); + HifiAction * getActionForOption(const QString& menuOption) { return new HifiAction(menuOption); } -// QMenu* addMenu(const QString& menuName); - //void removeMenu(const QString& menuName); - //bool menuExists(const QString& menuName); - //void addSeparator(const QString& menuName, const QString& separatorName); - //void removeSeparator(const QString& menuName, const QString& separatorName); - //void addMenuItem(const MenuItemProperties& properties); - //void removeMenuItem(const QString& menuitem); - //bool menuItemExists(const QString& menuName, const QString& menuitem); + + void addMenuItem(const MenuItemProperties& properties); + bool isOptionChecked(const QString& menuOption) const { return HifiMenu::isChecked(menuOption); } + void setIsOptionChecked(const QString& menuOption, bool isChecked) { HifiMenu::setChecked(menuOption, isChecked); } + void triggerOption(const QString& menuOption) { - HifiMenu::triggerMenuItem(menuOption); + HifiMenu::triggerItem(menuOption); } + void setOptionText(const QString& menuOption, const QString & text) { - HifiMenu::setText(menuOption, text); + HifiMenu::setItemText(menuOption, text); } + void setOptionTriggerAction(const QString& menuOption, std::function f) { HifiMenu::setTriggerAction(menuOption, f); } - //void setOptionToggleAction(const QString& menuOption, std::function f); - //void addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f); - //void addMenuItem(const QString & parentMenu, const QString & menuOption); - //void addMenu(const QString & parentMenu, const QString & menuOption); - //void enableMenuItem(const QString & menuOption, bool enabled = true); private: void init(); diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp index 6ca034522c..0c488e05a3 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/HifiMenu.cpp @@ -112,7 +112,7 @@ void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorN void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) { } -void HifiMenu::addMenuItem(const QString & parentMenu, const QString & menuOption) { +void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption) { QObject* parent = findMenuObject(parentMenu); Q_ASSERT(parent); QObject* result = ::addItem(parent, menuOption); @@ -127,20 +127,20 @@ void HifiMenu::addMenuItem(const QString & parentMenu, const QString & menuOptio connect(result, SIGNAL(toggled(bool)), &_toggleMapper, SLOT(map())); } -void HifiMenu::addMenuItem(const QString & parentMenu, const QString & menuOption, std::function f) { +void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, std::function f) { setTriggerAction(menuOption, f); - addMenuItem(parentMenu, menuOption); + addItem(parentMenu, menuOption); } -void HifiMenu::removeMenuItem(const QString& menuOption) { +void HifiMenu::removeItem(const QString& menuOption) { removeMenu(menuOption); } -bool HifiMenu::menuItemExists(const QString& menuName, const QString& menuitem) const { +bool HifiMenu::itemExists(const QString& menuName, const QString& menuitem) const { return findMenuObject(menuName); } -void HifiMenu::triggerMenuItem(const QString& menuOption) { +void HifiMenu::triggerItem(const QString& menuOption) { QObject* menuItem = findMenuObject(menuOption); Q_ASSERT(menuItem); Q_ASSERT(menuItem != _rootMenu); @@ -185,20 +185,24 @@ void HifiMenu::setCheckable(const QString& menuOption, bool checkable) { Q_ASSERT(menuItem->property("checkable").toBool() == checkable); } -void HifiMenu::setText(const QString& menuOption, const QString & text) { +void HifiMenu::setItemText(const QString& menuOption, const QString & text) { QObject* menuItem = findMenuObject(menuOption); if (!menuItem) { warn(menuOption); return; } - menuItem->setProperty("text", QVariant::fromValue(text)); + 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::enableMenuItem(const QString & menuOption, bool enabled) { +void HifiMenu::enableItem(const QString & menuOption, bool enabled) { QObject* menuItem = findMenuObject(menuOption); if (!menuItem) { warn(menuOption); @@ -207,15 +211,45 @@ void HifiMenu::enableMenuItem(const QString & menuOption, bool enabled) { menuItem->setProperty("enabled", QVariant::fromValue(enabled)); } -void HifiMenu::addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked) { - addMenuItem(parentMenu, menuOption); +void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) { + addItem(parentMenu, menuOption); setCheckable(menuOption); if (checked) { setChecked(menuOption, checked); } } -void HifiMenu::addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { +void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { setToggleAction(menuOption, f); - addCheckableMenuItem(parentMenu, menuOption, checked); + 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(); } diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h index 4d7d860224..299a927bc9 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -35,19 +35,26 @@ public: void addSeparator(const QString& menuName, const QString& separatorName); void removeSeparator(const QString& menuName, const QString& separatorName); - void addMenuItem(const QString& parentMenu, const QString& menuOption); - void addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked = false); - void addCheckableMenuItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f); - void addMenuItem(const QString& parentMenu, const QString& menuOption, std::function f); - void removeMenuItem(const QString& menuitem); - bool menuItemExists(const QString& menuName, const QString& menuitem) const; - void triggerMenuItem(const QString& menuOption); - void enableMenuItem(const QString& menuOption, bool enabled = true); + void addItem(const QString& parentMenu, const QString& menuOption); + void addItem(const QString& parentMenu, const QString& menuOption, std::function f); + + 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 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 isChecked = true); + 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 setText(const QString& menuOption, const QString& text); + 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); From 80d3f35b2eae56a0f6a5260a0ed15ccdc05dadc0 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 24 Apr 2015 09:57:22 -0700 Subject: [PATCH 11/33] Controller disabled when not in physics engine --- .../physics/src/DynamicCharacterController.cpp | 16 ++++++++++------ .../physics/src/DynamicCharacterController.h | 6 +++--- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index 46ff2e524e..fa1a2d42d1 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -43,8 +43,8 @@ protected: DynamicCharacterController::DynamicCharacterController(AvatarData* avatarData) { _halfHeight = 1.0f; - _shape = NULL; - _rigidBody = NULL; + _shape = nullptr; + _rigidBody = nullptr; assert(avatarData); _avatarData = avatarData; @@ -257,6 +257,10 @@ void DynamicCharacterController::setEnabled(bool enabled) { } } +bool DynamicCharacterController::isEnabled() const { + return _enabled && _dynamicsWorld; +} + void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { if (_dynamicsWorld) { @@ -264,7 +268,7 @@ void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { _dynamicsWorld->removeRigidBody(_rigidBody); _dynamicsWorld->removeAction(this); } - _dynamicsWorld = NULL; + _dynamicsWorld = nullptr; } if (world && _rigidBody) { _dynamicsWorld = world; @@ -294,9 +298,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; @@ -316,7 +320,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..bd71a8bcdc 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,7 @@ public: bool needsRemoval() const; bool needsAddition() const; void setEnabled(bool enabled); - bool isEnabled() const { return _enabled; } + bool isEnabled() const; void setDynamicsWorld(btDynamicsWorld* world); void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); From 1ff8c67576aa28d0443ef825c58d7f0f5dbacd35 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 24 Apr 2015 12:54:11 -0700 Subject: [PATCH 12/33] More work on menus and some code cleanup --- interface/resources/qml/HifiAction.qml | 28 +- interface/resources/qml/HifiMenu.qml | 521 +++++++----------- interface/resources/qml/MarketplaceDialog.qml | 10 +- interface/resources/qml/MessageDialog.qml | 28 +- interface/resources/qml/RootMenu.qml | 8 +- interface/resources/qml/TestRoot.qml | 18 +- interface/resources/qml/controls/Dialog.qml | 2 + .../resources/qml/controls/FontAwesome.qml | 2 +- .../resources/qml/styles/ButtonStyle.qml | 8 +- .../resources/qml/styles/HifiPalette.qml | 2 +- .../resources/qml/styles/MenuButtonStyle.qml | 8 +- interface/src/Application.cpp | 18 +- interface/src/MainWindow.cpp | 6 +- interface/src/Menu.cpp | 165 +++--- .../src/scripting/MenuScriptingInterface.cpp | 48 +- .../scripting/WindowScriptingInterface.cpp | 13 +- interface/src/ui/ApplicationOverlay.cpp | 2 +- libraries/shared/src/PathUtils.cpp | 12 +- libraries/ui/src/HifiMenu.cpp | 40 +- libraries/ui/src/HifiMenu.h | 10 +- tests/ui/main.qml | 2 +- 21 files changed, 438 insertions(+), 513 deletions(-) diff --git a/interface/resources/qml/HifiAction.qml b/interface/resources/qml/HifiAction.qml index 8e6e7d59b4..0cecff91d7 100644 --- a/interface/resources/qml/HifiAction.qml +++ b/interface/resources/qml/HifiAction.qml @@ -2,19 +2,19 @@ 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); - } + 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 index 254ae32520..d86821601a 100644 --- a/interface/resources/qml/HifiMenu.qml +++ b/interface/resources/qml/HifiMenu.qml @@ -6,19 +6,20 @@ import "controls" import "styles" Hifi.HifiMenu { - id: root - anchors.fill: parent - objectName: "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 && columns.length == 0) { + pushColumn(rootMenu.items); + } + opacity = enabled ? 1.0 : 0.0 if (enabled) { forceActiveFocus() } @@ -37,355 +38,235 @@ Hifi.HifiMenu { } onVisibleChanged: { - if (!visible) reset(); + if (!visible) reset(); } property var menu: Menu {} property var models: [] - property var columns: [] + 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(); - } - } + 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" - } - + 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 + 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); - } - } - } + onClicked: { + listView.currentIndex = listViewIndex + parent.root.selectItem(parent.source); + } + } + } } - + property var menuBuilder: Component { - Border { - SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } - x: root.models.length * 60; + 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 + 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 + 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; - Behavior on y { - NumberAnimation { - duration: 100 - easing.type: Easing.InOutQuint - } - } - } - + 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 - } - } + 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]; + 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 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; - } + 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(); + 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(); - } - } + 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(); + } + } - /* - HifiMenu { - id: rootMenu - Menu { - id: menu - Menu { - title: "File" - MenuItem { - action: HifiAction { - name: rootMenu.login - } - } - MenuItem { - action: HifiAction { - name: "Test" - checkable: true - } - } - MenuItem { - action: HifiAction { - name: rootMenu.quit - } - } - } - Menu { - title: "Edit" - MenuItem { - action: HifiAction { - text: "Copy" - shortcut: StandardKey.Copy - } - } - MenuItem { - action: HifiAction { - text: "Cut" - shortcut: StandardKey.Cut - } - } - MenuItem { - action: HifiAction { - text: "Paste" - shortcut: StandardKey.Paste - } - } - MenuItem { - action: HifiAction { - text: "Undo" - shortcut: StandardKey.Undo - } - } - MenuItem { - action: HifiAction { - text: "Redo" - shortcut: StandardKey.Redo - } - } - MenuItem { - action: HifiAction { - name: rootMenu.attachments - } - } - MenuItem { - action: HifiAction { - name: rootMenu.animations - } - } - } - - Menu { - title: "Scripts" - MenuItem { - action: HifiAction { - name: rootMenu.scriptEditor - } - } - MenuItem { - action: HifiAction { - name: rootMenu.loadScript - } - } - MenuItem { - action: HifiAction { - name: rootMenu.loadScriptURL - } - } - MenuItem { - action: HifiAction { - name: rootMenu.stopAllScripts - } - } - MenuItem { - action: HifiAction { - name: rootMenu.reloadAllScripts - } - } - MenuItem { - action: HifiAction { - name: rootMenu.runningScripts - } - } - } - Menu { - title: "Location" - MenuItem { - action: HifiAction { - name: rootMenu.addressBar - } - } - MenuItem { - action: HifiAction { - name: rootMenu.copyAddress - } - } - MenuItem { - action: HifiAction { - name: rootMenu.copyPath - } - } - } - } - } - */ - Keys.onPressed: { - switch (event.key) { - case Qt.Key_Escape: - root.popColumn() + switch (event.key) { + case Qt.Key_Escape: + root.popColumn() event.accepted = true; - } + } } - MouseArea { - anchors.fill: parent - id: mouseArea - acceptedButtons: Qt.LeftButton | Qt.RightButton - onClicked: { + MouseArea { + anchors.fill: parent + id: mouseArea + acceptedButtons: Qt.LeftButton | Qt.RightButton + onClicked: { if (mouse.button == Qt.RightButton) { - root.popColumn(); + root.popColumn(); } else { - root.enabled = false; - } - } - } + root.enabled = false; + } + } + } } diff --git a/interface/resources/qml/MarketplaceDialog.qml b/interface/resources/qml/MarketplaceDialog.qml index fe192179eb..58bb3e6183 100644 --- a/interface/resources/qml/MarketplaceDialog.qml +++ b/interface/resources/qml/MarketplaceDialog.qml @@ -14,7 +14,7 @@ Dialog { resizable: true MarketplaceDialog { - id: marketplaceDialog + id: marketplaceDialog } Item { @@ -33,13 +33,13 @@ Dialog { url: "https://metaverse.highfidelity.com/marketplace" anchors.fill: parent onNavigationRequested: { - console.log(request.url) + console.log(request.url) if (!marketplaceDialog.navigationRequested(request.url)) { - console.log("Application absorbed the request") + console.log("Application absorbed the request") request.action = WebView.IgnoreRequest; - return; + return; } - console.log("Application passed on the request") + console.log("Application passed on the request") request.action = WebView.AcceptRequest; return; } diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index eaf304b16a..26fd30db27 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -71,13 +71,13 @@ Dialog { onEnabledChanged: { if (enabled) { - content.forceActiveFocus(); + content.forceActiveFocus(); } } Hifi.MessageDialog { - id: content - clip: true + id: content + clip: true anchors.fill: parent anchors.topMargin: parent.topMargin + root.outerSpacing anchors.leftMargin: parent.margins + root.outerSpacing @@ -88,16 +88,16 @@ Dialog { property real buttonsRowImplicitWidth: Screen.pixelDensity * 50 Keys.onPressed: { - console.log("Key press at content") + console.log("Key press at content") event.accepted = true if (event.modifiers === Qt.ControlModifier) switch (event.key) { case Qt.Key_A: - console.log("Select All") + console.log("Select All") detailedText.selectAll() break case Qt.Key_C: - console.log("Copy") + console.log("Copy") detailedText.copy() break case Qt.Key_Period: @@ -107,12 +107,12 @@ Dialog { } else switch (event.key) { case Qt.Key_Escape: case Qt.Key_Back: - console.log("Rejecting") + console.log("Rejecting") reject() break case Qt.Key_Enter: case Qt.Key_Return: - console.log("Accepting") + console.log("Accepting") accept() break } @@ -123,18 +123,18 @@ Dialog { Component.onCompleted: { root.title = title } - + onTitleChanged: { - root.title = title + root.title = title } - + Column { id: contentColumn spacing: root.outerSpacing anchors { - top: parent.top - left: parent.left - right: parent.right + top: parent.top + left: parent.left + right: parent.right } Item { diff --git a/interface/resources/qml/RootMenu.qml b/interface/resources/qml/RootMenu.qml index dc2e36f89a..b8c81a6589 100644 --- a/interface/resources/qml/RootMenu.qml +++ b/interface/resources/qml/RootMenu.qml @@ -2,8 +2,8 @@ import QtQuick 2.4 import QtQuick.Controls 1.3 Item { - Menu { - id: root - objectName: "rootMenu" - } + Menu { + id: root + objectName: "rootMenu" + } } diff --git a/interface/resources/qml/TestRoot.qml b/interface/resources/qml/TestRoot.qml index 0b0f890361..80f8c900e3 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -10,17 +10,17 @@ Root { anchors.fill: parent onWidthChanged: { - console.log("Root width: " + width) - } - onHeightChanged: { - console.log("Root height: " + height) - } + console.log("Root width: " + width) + } + onHeightChanged: { + console.log("Root height: " + height) + } Component.onCompleted: { - console.log("Completed root") - root.forceActiveFocus() + console.log("Completed root") + root.forceActiveFocus() } - + Button { id: messageBox anchors.right: createDialog.left @@ -48,7 +48,7 @@ Root { } Keys.onPressed: { - console.log(event.key); + console.log(event.key); } } diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 109889f738..07162ad1d8 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -16,6 +16,8 @@ Item { HifiPalette { id: hifiPalette } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } + x: parent ? parent.width / 2 - width / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 property int animationDuration: 400 property bool destroyOnInvisible: false diff --git a/interface/resources/qml/controls/FontAwesome.qml b/interface/resources/qml/controls/FontAwesome.qml index 16fd4a6ecc..e975c0342b 100644 --- a/interface/resources/qml/controls/FontAwesome.qml +++ b/interface/resources/qml/controls/FontAwesome.qml @@ -7,7 +7,7 @@ Text { FontLoader { id: iconFont; source: "../../fonts/fontawesome-webfont.ttf"; } property int size: 32 width: size - height: size + height: size verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter font.family: iconFont.name diff --git a/interface/resources/qml/styles/ButtonStyle.qml b/interface/resources/qml/styles/ButtonStyle.qml index caf366364a..8d866390a0 100644 --- a/interface/resources/qml/styles/ButtonStyle.qml +++ b/interface/resources/qml/styles/ButtonStyle.qml @@ -2,15 +2,15 @@ import QtQuick 2.4 as Original import QtQuick.Controls.Styles 1.3 as OriginalStyles import "." import "../controls" - + OriginalStyles.ButtonStyle { - Original.SystemPalette { id: myPalette; colorGroup: Original.SystemPalette.Active } - padding { + Original.SystemPalette { id: myPalette; colorGroup: Original.SystemPalette.Active } + padding { top: 8 left: 12 right: 12 bottom: 8 - } + } background: Border { anchors.fill: parent } diff --git a/interface/resources/qml/styles/HifiPalette.qml b/interface/resources/qml/styles/HifiPalette.qml index 641d533e3d..46ef0ef14e 100644 --- a/interface/resources/qml/styles/HifiPalette.qml +++ b/interface/resources/qml/styles/HifiPalette.qml @@ -1,5 +1,5 @@ import QtQuick 2.4 QtObject { - property string hifiBlue: "#0e7077" + property string hifiBlue: "#0e7077" } \ No newline at end of file diff --git a/interface/resources/qml/styles/MenuButtonStyle.qml b/interface/resources/qml/styles/MenuButtonStyle.qml index f30ade3d33..fd21e88d86 100644 --- a/interface/resources/qml/styles/MenuButtonStyle.qml +++ b/interface/resources/qml/styles/MenuButtonStyle.qml @@ -2,15 +2,15 @@ import QtQuick 2.4 import QtQuick.Controls.Styles 1.3 import "../controls" import "." - + ButtonStyle { - HifiPalette { id: hifiPalette } - padding { + HifiPalette { id: hifiPalette } + padding { top: 2 left: 4 right: 4 bottom: 2 - } + } background: Item {} label: Text { renderType: Text.NativeRendering diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d281282fc8..8df8009b95 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1079,12 +1079,12 @@ bool Application::eventFilter(QObject* object, QEvent* event) { return false; } -static bool _altPressed; -static bool _ctrlPressed; +static bool altPressed; +static bool ctrlPressed; void Application::keyPressEvent(QKeyEvent* event) { - _altPressed = event->key() == Qt::Key_Alt; - _ctrlPressed = event->key() == Qt::Key_Control; + altPressed = event->key() == Qt::Key_Alt; + ctrlPressed = event->key() == Qt::Key_Control; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1336,15 +1336,13 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_Alt && _altPressed) { + if (event->key() == Qt::Key_Alt && altPressed && _window->isActiveWindow()) { Menu::toggle(); } - if (event->key() == Qt::Key_Control && _ctrlPressed) { - auto offscreenUi = DependencyManager::get(); - auto rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); - QMetaObject::invokeMethod(rootMenu, "popup"); + if (event->key() == Qt::Key_Control && ctrlPressed && _window->isActiveWindow()) { + Menu::toggle(); } - _ctrlPressed = event->key() == Qt::Key_Control; + ctrlPressed = altPressed = false; _keysPressed.remove(event->key()); diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index f60716dc48..7e8d46f5ee 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -94,9 +94,9 @@ void MainWindow::changeEvent(QEvent* event) { emit windowShown(true); } - if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { - Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); - } + //if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { + // Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); + //} } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { emit windowShown(true); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 029c150884..8c45f583f1 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -45,13 +45,12 @@ #include "Util.h" // Proxy object to simplify porting over -HifiAction::HifiAction(const QString & menuOption) : _menuOption(menuOption) { +HifiAction::HifiAction(const QString& menuOption) : _menuOption(menuOption) { } -//void HifiAction::setCheckable(bool) { -// Menu::getInstance()->set -// qFatal("Not implemented"); -//} +void HifiAction::setCheckable(bool checkable) { + Menu::getInstance()->setCheckable(_menuOption, checkable); +} void HifiAction::setChecked(bool checked) { Menu::getInstance()->setChecked(_menuOption, checked); @@ -65,7 +64,7 @@ QString HifiAction::shortcut() const { return Menu::getInstance()->getItemShortcut(_menuOption); } -void HifiAction::setText(const QString & text) { +void HifiAction::setText(const QString& text) { Menu::getInstance()->setItemText(_menuOption, text); } @@ -101,7 +100,7 @@ Menu* Menu::getInstance() { return _instance; } -Menu::Menu(QQuickItem * parent) : HifiMenu(parent) { +Menu::Menu(QQuickItem* parent) : HifiMenu(parent) { } void Menu::init() { @@ -125,12 +124,15 @@ void Menu::init() { #endif { +#if 0 static const QString SCRIPTS_MENU{ "Scripts" }; - addMenu(FILE_MENU, SCRIPTS_MENU); + addMenu(FILE_MENU, LOCATION_MENU); +#else + static const QString SCRIPTS_MENU{ FILE_MENU }; + addSeparator(FILE_MENU, "Scripts"); +#endif //Qt::CTRL | Qt::Key_O - addItem(SCRIPTS_MENU, MenuOption::LoadScript, [=] { - qApp->loadDialog(); - }); + addItem(SCRIPTS_MENU, MenuOption::LoadScript, qApp, SLOT(loadDialog())); //Qt::CTRL | Qt::SHIFT | Qt::Key_O addItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { qApp->loadScriptURLDialog(); @@ -149,9 +151,15 @@ void Menu::init() { } { +#if 0 static const QString LOCATION_MENU{ "Location" }; addMenu(FILE_MENU, LOCATION_MENU); +#else + addSeparator(FILE_MENU, "Location"); + static const QString LOCATION_MENU{ FILE_MENU }; +#endif qApp->getBookmarks()->setupMenus(LOCATION_MENU); + //Qt::CTRL | Qt::Key_L addItem(LOCATION_MENU, MenuOption::AddressBar, [=] { auto dialogsManager = DependencyManager::get(); @@ -178,16 +186,15 @@ void Menu::init() { { static const QString EDIT_MENU{ "Edit" }; addMenu(ROOT_MENU, EDIT_MENU); -#if 0 - QUndoStack* undoStack = qApp->getUndoStack(); - QAction* undoAction = undoStack->createUndoAction(editMenu); - undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); - addActionToQMenuAndActionHash(editMenu, undoAction); - QAction* redoAction = undoStack->createRedoAction(editMenu); - redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); - addActionToQMenuAndActionHash(editMenu, redoAction); -#endif + QUndoStack* undoStack = qApp->getUndoStack(); + QAction* undoAction = undoStack->createUndoAction(this); + //undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); + addItem(EDIT_MENU, undoAction->text(), undoAction, SLOT(trigger())); + + QAction* redoAction = undoStack->createRedoAction(this); + //redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); + addItem(EDIT_MENU, redoAction->text(), redoAction, SLOT(trigger())); // Qt::CTRL | Qt::Key_Comma // QAction::PreferencesRole @@ -231,23 +238,20 @@ void Menu::init() { // FIXME group addCheckableItem(VIZ_MENU, MenuOption::VisibleToEveryone, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, - [=](bool) { discoverabilityManager->setVisibility(); }); + discoverabilityManager.data(), SLOT(setVisibility())); addCheckableItem(VIZ_MENU, MenuOption::VisibleToFriends, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, - [=](bool) { discoverabilityManager->setVisibility(); }); - addCheckableItem(VIZ_MENU, MenuOption::VisibleToNoOne, + discoverabilityManager.data(), SLOT(setVisibility())); + addCheckableItem(VIZ_MENU, MenuOption::VisibleToNoOne, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, - [=](bool) { discoverabilityManager->setVisibility(); }); - + discoverabilityManager.data(), SLOT(setVisibility())); connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); - } //Qt::CTRL | Qt::ALT | Qt::Key_T, - addItem(TOOLS_MENU, MenuOption::ToolWindow, [=] { -// dialogsManager->toggleToolWindow(); - }); + addItem(TOOLS_MENU, MenuOption::ToolWindow, + dialogsManager.data(), SLOT(toggleToolWindow())); //Qt::CTRL | Qt::ALT | Qt::Key_J, addItem(TOOLS_MENU, MenuOption::Console, [=] { @@ -297,12 +301,10 @@ void Menu::init() { addCheckableItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); addCheckableItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); addCheckableItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); - addCheckableItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, [=](bool) { - avatar->updateMotionBehavior(); - }); - addCheckableItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, [=](bool) { - avatar->updateMotionBehavior(); - }); + addCheckableItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, + avatar, SLOT(updateMotionBehavior())); + addCheckableItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, + avatar, SLOT(updateMotionBehavior())); } { @@ -311,32 +313,28 @@ void Menu::init() { // Mac Qt::CTRL | Qt::META | Qt::Key_F, // Win32/Linux Qt::CTRL | Qt::Key_F, - addCheckableItem(VIEW_MENU, MenuOption::Fullscreen, false, [=](bool checked) { -// qApp->setFullscreen(checked); - }); + addCheckableItem(VIEW_MENU, MenuOption::Fullscreen, false); + connectCheckable(MenuOption::Fullscreen, qApp, SLOT(setFullscreen(bool))); // QML Qt::Key_P, - addCheckableItem(VIEW_MENU, MenuOption::FirstPerson, true, [=](bool checked) { -// qApp->cameraMenuChanged(); - }); + addCheckableItem(VIEW_MENU, MenuOption::FirstPerson, true, + qApp, SLOT(cameraMenuChanged())); //QML Qt::SHIFT | Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::Mirror, true); - + addCheckableItem(VIEW_MENU, MenuOption::Mirror, true, + qApp, SLOT(cameraMenuChanged())); // QML Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::FullscreenMirror, true, [=](bool checked) { -// qApp->cameraMenuChanged(); - }); + addCheckableItem(VIEW_MENU, MenuOption::FullscreenMirror, false, + qApp, SLOT(cameraMenuChanged())); + // Mac Qt::META | Qt::Key_H, // Win32/Linux Qt::CTRL | Qt::Key_H, addCheckableItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { dialogsManager->hmdTools(checked); }); - addCheckableItem(VIEW_MENU, MenuOption::EnableVRMode, false, [=](bool checked) { -// qApp->setEnableVRMode(checked); - }); - addCheckableItem(VIEW_MENU, MenuOption::Enable3DTVMode, false, [=](bool checked) { -// qApp->setEnable3DTVMode(checked); - }); + addCheckableItem(VIEW_MENU, MenuOption::EnableVRMode, false); + connectCheckable(MenuOption::EnableVRMode, qApp, SLOT(setEnableVRMode(bool))); + addCheckableItem(VIEW_MENU, MenuOption::Enable3DTVMode, false); + connectCheckable(MenuOption::Enable3DTVMode, qApp, SLOT(setEnable3DTVMode(bool))); { static const QString BORDER_MENU{ "Server Borders" }; @@ -362,7 +360,6 @@ void Menu::init() { dialogsManager->octreeStatsDetails(); }); } - { static const QString DEV_MENU{ "Developer" }; @@ -561,25 +558,19 @@ void Menu::init() { static const QString AUDIO_MENU{ "Audio" }; addMenu(DEV_MENU, AUDIO_MENU); auto audioIO = DependencyManager::get(); - addCheckableItem(AUDIO_MENU, MenuOption::AudioNoiseReduction, true, [=](bool checked) { - audioIO->toggleAudioNoiseReduction(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::EchoServerAudio, false, [=](bool checked) { - audioIO->toggleServerEcho(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::EchoLocalAudio, false, [=](bool checked) { - audioIO->toggleLocalEcho(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::StereoAudio, false, [=](bool checked) { - audioIO->toggleStereoInput(); - }); + addCheckableItem(AUDIO_MENU, MenuOption::AudioNoiseReduction, true, + audioIO.data(), SLOT(toggleAudioNoiseReduction())); + addCheckableItem(AUDIO_MENU, MenuOption::EchoServerAudio, false, + audioIO.data(), SLOT(toggleServerEcho())); + addCheckableItem(AUDIO_MENU, MenuOption::EchoLocalAudio, false, + audioIO.data(), SLOT(toggleLocalEcho())); + addCheckableItem(AUDIO_MENU, MenuOption::StereoAudio, false, + audioIO.data(), SLOT(toggleStereoInput())); // Qt::CTRL | Qt::Key_M, - addCheckableItem(AUDIO_MENU, MenuOption::MuteAudio, false, [=](bool checked) { - audioIO->toggleMute(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::MuteEnvironment, false, [=](bool checked) { - audioIO->sendMuteEnvironmentPacket(); - }); + addCheckableItem(AUDIO_MENU, MenuOption::MuteAudio, false, + audioIO.data(), SLOT(toggleMute())); + addCheckableItem(AUDIO_MENU, MenuOption::MuteEnvironment, false, + audioIO.data(), SLOT(sendMuteEnvironmentPacket())); { static const QString SCOPE_MENU{ "Audio Scope" }; addMenu(AUDIO_MENU, SCOPE_MENU); @@ -637,6 +628,37 @@ void Menu::loadSettings() { void Menu::saveSettings() { // scanMenuBar(&Menu::saveAction); } + +void Menu::addMenuItem(const MenuItemProperties& properties) { + if (QThread::currentThread() != Application::getInstance()->getMainThread()) { + Application::getInstance()->postLambdaEvent([=]{ + addMenuItem(properties); + }); + return; + } + // Shortcut key items: in order of priority + //QString shortcutKey; + //KeyEvent shortcutKeyEvent; + //QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above + + //// location related items: in order of priority + //int position; + //QString beforeItem; + //QString afterItem; + + if (properties.isSeparator) { + addSeparator(properties.menuName, properties.menuItemName); + return; + } + addItem(properties.menuName, properties.menuItemName); + if (properties.isCheckable) { + setCheckable(properties.menuItemName); + if (properties.isChecked) { + setChecked(properties.menuItemName); + } + } + } + #if 0 void Menu::loadAction(Settings& settings, QAction& action) { @@ -688,4 +710,5 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } } + #endif diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 277c611f04..823bb9f8d0 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -28,72 +28,50 @@ void MenuScriptingInterface::menuItemTriggered() { } void MenuScriptingInterface::addMenu(const QString& menu) { - QMetaObject::invokeMethod(Menu::getInstance(), "addMenu", Q_ARG(const QString&, menu)); + Menu::getInstance()->addMenu("", menu); } void MenuScriptingInterface::removeMenu(const QString& menu) { - QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu)); + Menu::getInstance()->removeMenu(menu); } bool MenuScriptingInterface::menuExists(const QString& menu) { - bool result; - QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menu)); - return result; + return Menu::getInstance()->menuExists(menu); } void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) { - QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator", - Q_ARG(const QString&, menuName), - Q_ARG(const QString&, separatorName)); + Menu::getInstance()->addSeparator(menuName, separatorName); } void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) { - QMetaObject::invokeMethod(Menu::getInstance(), "removeSeparator", - Q_ARG(const QString&, menuName), - Q_ARG(const QString&, separatorName)); + Menu::getInstance()->removeSeparator(menuName, separatorName); } void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) { - QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); + Menu::getInstance()->addMenuItem(properties); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) { - MenuItemProperties properties(menu, menuitem, shortcutKey); - QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); + Menu::getInstance()->addItem(menu, menuitem); + Menu::getInstance()->setItemShortcut(menuitem, shortcutKey); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) { - MenuItemProperties properties(menu, menuitem); - QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); + Menu::getInstance()->addItem(menu, menuitem); } void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) { - QMetaObject::invokeMethod(Menu::getInstance(), "removeMenuItem", - Q_ARG(const QString&, menu), - Q_ARG(const QString&, menuitem)); + Menu::getInstance()->removeItem(menuitem); }; bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { - bool result; - QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menu), - Q_ARG(const QString&, menuitem)); - return result; + return Menu::getInstance()->itemExists(menu, menuitem); } bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { - bool result; - QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, - Q_RETURN_ARG(bool, result), - Q_ARG(const QString&, menuOption)); - return result; + return Menu::getInstance()->isChecked(menuOption); } void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { - QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, - Q_ARG(const QString&, menuOption), - Q_ARG(bool, isChecked)); + Menu::getInstance()->setChecked(menuOption, isChecked); } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 46ca0c160f..5e747f216d 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -45,13 +45,18 @@ QScriptValue WindowScriptingInterface::hasFocus() { } void WindowScriptingInterface::setFocus() { - auto window = Application::getInstance()->getWindow(); - window->activateWindow(); - window->setFocus(); + 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/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 2a8f01aafc..7940a06993 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -323,7 +323,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) { //Render magnifier, but dont show border for mouse magnifier glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(), _reticlePosition[MOUSE].y())); - with_each_texture(_overlays.getTexture(), _newUiTexture, [&] { + with_each_texture(_overlays.getTexture(), 0, [&] { renderMagnifier(projection, _magSizeMult[i], i != MOUSE); }); } diff --git a/libraries/shared/src/PathUtils.cpp b/libraries/shared/src/PathUtils.cpp index bf846c0bf2..47e5659c60 100644 --- a/libraries/shared/src/PathUtils.cpp +++ b/libraries/shared/src/PathUtils.cpp @@ -14,16 +14,26 @@ #include #include #include - +#include #include "PathUtils.h" QString& PathUtils::resourcesPath() { +#ifdef DEBUG + static QString staticResourcePath; + if (staticResourcePath.isEmpty()) { + QDir path(__FILE__); + path.cdUp(); + staticResourcePath = path.cleanPath(path.absoluteFilePath("../../../interface/resources/")) + "/"; + } +#else #ifdef Q_OS_MAC static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/../Resources/"; #else static QString staticResourcePath = QCoreApplication::applicationDirPath() + "/resources/"; #endif +#endif + return staticResourcePath; } diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp index 0c488e05a3..27517f99d7 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/HifiMenu.cpp @@ -45,7 +45,7 @@ void HifiMenu::setTriggerAction(const QString & name, std::function f) { QObject* addMenu(QObject* parent, const QString & text) { // FIXME add more checking here to ensure no name conflicts QVariant returnedValue; - QMetaObject::invokeMethod(parent, "addMenu", + QMetaObject::invokeMethod(parent, "addMenu", Qt::DirectConnection, Q_RETURN_ARG(QVariant, returnedValue), Q_ARG(QVariant, text)); QObject* result = returnedValue.value(); @@ -59,7 +59,7 @@ 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", + bool invokeResult = QMetaObject::invokeMethod(parent, "addItem", Qt::DirectConnection, Q_RETURN_ARG(QQuickMenuItem*, returnedValue), Q_ARG(QString, text)); Q_ASSERT(invokeResult); @@ -104,9 +104,11 @@ bool HifiMenu::menuExists(const QString& menuName) const { } void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorName) { - // FIXME 'add sep' -// addMenu(parentMenu, separatorName); -// setEnabled() + 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) { @@ -132,6 +134,11 @@ void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, s 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); } @@ -170,8 +177,10 @@ void HifiMenu::setChecked(const QString& menuOption, bool isChecked) { warn(menuOption); return; } - menuItem->setProperty("checked", QVariant::fromValue(isChecked)); - Q_ASSERT(menuItem->property("checked").toBool() == isChecked); + 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) { @@ -185,7 +194,7 @@ void HifiMenu::setCheckable(const QString& menuOption, bool checkable) { Q_ASSERT(menuItem->property("checkable").toBool() == checkable); } -void HifiMenu::setItemText(const QString& menuOption, const QString & text) { +void HifiMenu::setItemText(const QString& menuOption, const QString& text) { QObject* menuItem = findMenuObject(menuOption); if (!menuItem) { warn(menuOption); @@ -253,3 +262,18 @@ QString HifiMenu::getItemShortcut(const QString& menuOption) { } 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 index 299a927bc9..c89c91b028 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -37,9 +37,13 @@ public: 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; @@ -48,15 +52,15 @@ public: 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 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); + void setItemShortcut(const QString& menuOption, const QString& shortcut); QString getItemShortcut(const QString& menuOption); - void setRootMenu(QObject * rootMenu); + void setRootMenu(QObject* rootMenu); private slots: void onTriggeredByName(const QString& name); diff --git a/tests/ui/main.qml b/tests/ui/main.qml index 89dac3d1af..168b9fb291 100644 --- a/tests/ui/main.qml +++ b/tests/ui/main.qml @@ -47,7 +47,7 @@ import "qml" //import "/Users/bdavis/Git/hifi/interface/resources/qml" Item { - anchors.fill: parent + anchors.fill: parent visible: true //title: "Qt Quick Controls Gallery" From ee30588fd47c498bb598a0c64ee5ffb2f12eb653 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 25 Apr 2015 10:41:06 -0700 Subject: [PATCH 13/33] More menu work --- interface/resources/qml/HifiAction.qml | 20 - interface/resources/qml/HifiMenu.qml | 177 ++-- interface/resources/qml/Menu.qml | 1072 ++++++++++++++++++++++++ interface/src/Application.cpp | 11 - interface/src/ui/LoginDialog.cpp | 4 +- interface/src/ui/MarketplaceDialog.cpp | 2 +- interface/src/ui/MarketplaceDialog.h | 2 +- interface/src/ui/MenuQml.cpp | 19 - interface/src/ui/MenuQml.h | 26 - libraries/ui/src/HifiMenu.cpp | 23 + libraries/ui/src/HifiMenu.h | 3 + libraries/ui/src/OffscreenUi.cpp | 7 +- libraries/ui/src/OffscreenUi.h | 22 +- tests/ui/src/main.cpp | 187 ++++- 14 files changed, 1376 insertions(+), 199 deletions(-) delete mode 100644 interface/resources/qml/HifiAction.qml create mode 100644 interface/resources/qml/Menu.qml delete mode 100644 interface/src/ui/MenuQml.cpp delete mode 100644 interface/src/ui/MenuQml.h 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 index 1afa39c5d1..2c6963a349 100644 --- a/interface/resources/qml/HifiMenu.qml +++ b/interface/resources/qml/HifiMenu.qml @@ -17,7 +17,7 @@ Hifi.HifiMenu { onEnabledChanged: { if (enabled && columns.length == 0) { - pushColumn(rootMenu.items); + pushColumn(rootMenu.menus); } opacity = enabled ? 1.0 : 0.0 if (enabled) { @@ -42,9 +42,95 @@ Hifi.HifiMenu { } - property var menu: Menu {} property var models: [] property var columns: [] + + 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 - 32; + 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 + } + } + + } + + } + } + property var itemBuilder: Component { Text { SystemPalette { id: sp; colorGroup: SystemPalette.Active } @@ -93,6 +179,8 @@ Hifi.HifiMenu { MouseArea { id: mouseArea acceptedButtons: Qt.LeftButton + anchors.left: parent.left + anchors.leftMargin: -32 anchors.bottom: parent.bottom anchors.bottomMargin: 0 anchors.top: parent.top @@ -106,91 +194,6 @@ Hifi.HifiMenu { } } - - 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() { diff --git a/interface/resources/qml/Menu.qml b/interface/resources/qml/Menu.qml new file mode 100644 index 0000000000..3939794fb2 --- /dev/null +++ b/interface/resources/qml/Menu.qml @@ -0,0 +1,1072 @@ +import QtQuick 2.4 +import QtQuick.Controls 1.3 +import Hifi 1.0 + +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" + } + + // Scripts + Action { + id: loadScript + objectName: "HifiAction_" + MenuConstants.LoadScript + text: qsTr("Open and Run Script File...") + shortcut: "Ctrl+O" + onTriggered: { + console.log("Shortcut ctrl+o hit"); + } + } + Action { + id: loadScriptURL + objectName: "HifiAction_" + MenuConstants.LoadScriptURL + text: qsTr("Open and Run Script from URL...") + shortcut: "Ctrl+Shift+O" + } + Action { + id: reloadAllScripts + objectName: "HifiAction_" + MenuConstants.ReloadAllScripts + text: qsTr("Reload All Scripts") + } + Action { + id: runningScripts + objectName: "HifiAction_" + MenuConstants.RunningScripts + text: qsTr("Running Scripts") + } + Action { + id: stopAllScripts + objectName: "HifiAction_" + MenuConstants.StopAllScripts + text: qsTr("Stop All Scripts") + } + + // Locations + Action { + id: bookmarkLocation + objectName: "HifiAction_" + MenuConstants.BookmarkLocation + text: qsTr("Bookmark Location") + } + Action { + id: bookmarks + objectName: "HifiAction_" + MenuConstants.Bookmarks + text: qsTr("Bookmarks") + } + Action { + id: addressBar + objectName: "HifiAction_" + MenuConstants.AddressBar + text: qsTr("Show Address Bar") + } + + // + // 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 + objectName: "HifiAction_" + MenuConstants.Attachments + text: qsTr("Attachments...") + } + + // + // Tools menu + // + Action { + id: scriptEditor + objectName: "HifiAction_" + MenuConstants.ScriptEditor + text: qsTr("Script Editor...") + } + Action { + id: controlWithSpeech + objectName: "HifiAction_" + MenuConstants.ControlWithSpeech + text: qsTr("Control With Speech") + } + Action { + id: chat + objectName: "HifiAction_" + MenuConstants.Chat + text: qsTr("Chat...") + } + Action { + id: addRemoveFriends + objectName: "HifiAction_" + MenuConstants.AddRemoveFriends + text: qsTr("Add/Remove Friends...") + } + ExclusiveGroup { + Action { + id: visibleToEveryone + objectName: "HifiAction_" + MenuConstants.VisibleToEveryone + text: qsTr("Everyone") + } + Action { + id: visibleToFriends + objectName: "HifiAction_" + MenuConstants.VisibleToFriends + text: qsTr("Friends") + } + Action { + id: visibleToNoOne + objectName: "HifiAction_" + MenuConstants.VisibleToNoOne + text: qsTr("No one") + } + } + Action { + id: toolWindow + objectName: "HifiAction_" + MenuConstants.ToolWindow + text: qsTr("Tool Window") + } + Action { + id: javascriptConsole + objectName: "HifiAction_" + MenuConstants.JavascriptConsole + text: qsTr("Console...") + } + Action { + id: resetSensors + objectName: "HifiAction_" + MenuConstants.ResetSensors + text: qsTr("Reset Sensors") + } + + + + + + + + + + + + + Action { + id: alignForearmsWithWrists + objectName: "HifiAction_" + MenuConstants.AlignForearmsWithWrists + text: qsTr("Align Forearms with Wrists") + checkable: true + } + Action { + id: alternateIK + objectName: "HifiAction_" + MenuConstants.AlternateIK + text: qsTr("Alternate IK") + checkable: true + } + Action { + id: ambientOcclusion + objectName: "HifiAction_" + MenuConstants.AmbientOcclusion + text: qsTr("Ambient Occlusion") + checkable: true + } + Action { + id: atmosphere + objectName: "HifiAction_" + MenuConstants.Atmosphere + text: qsTr("Atmosphere") + } + Action { + id: audioNoiseReduction + objectName: "HifiAction_" + MenuConstants.AudioNoiseReduction + text: qsTr("Audio Noise Reduction") + checkable: true + } + Action { + id: audioScope + objectName: "HifiAction_" + MenuConstants.AudioScope + text: qsTr("Show Scope") + checkable: true + } + ExclusiveGroup { + Action { + id: audioScopeFiveFrames + objectName: "HifiAction_" + MenuConstants.AudioScopeFiveFrames + text: qsTr("Five") + checkable: true + checked: true + } + Action { + id: audioScopeFiftyFrames + objectName: "HifiAction_" + MenuConstants.AudioScopeFiftyFrames + text: qsTr("Fifty") + checkable: true + } + Action { + id: audioScopeTwentyFrames + objectName: "HifiAction_" + MenuConstants.AudioScopeTwentyFrames + text: qsTr("Twenty") + checkable: true + } + } + Action { + id: audioScopePause + objectName: "HifiAction_" + MenuConstants.AudioScopePause + text: qsTr("Pause Scope") + checkable: true + } + Action { + id: audioStats + objectName: "HifiAction_" + MenuConstants.AudioStats + text: qsTr("Audio Stats") + checkable: true + } + Action { + id: audioStatsShowInjectedStreams + objectName: "HifiAction_" + MenuConstants.AudioStatsShowInjectedStreams + text: qsTr("Audio Stats Show Injected Streams") + checkable: true + } + Action { + id: bandwidthDetails + objectName: "HifiAction_" + MenuConstants.BandwidthDetails + text: qsTr("Bandwidth Details") + checkable: true + } + Action { + id: blueSpeechSphere + objectName: "HifiAction_" + MenuConstants.BlueSpeechSphere + text: qsTr("Blue Sphere While Speaking") + checkable: true + } + Action { + id: cascadedShadows + objectName: "HifiAction_" + MenuConstants.CascadedShadows + text: qsTr("Cascaded") + } + Action { + id: cachesSize + objectName: "HifiAction_" + MenuConstants.CachesSize + text: qsTr("RAM Caches Size") + } + Action { + id: collisions + objectName: "HifiAction_" + MenuConstants.Collisions + text: qsTr("Collisions") + } + Action { + id: copyAddress + objectName: "HifiAction_" + MenuConstants.CopyAddress + text: qsTr("Copy Address to Clipboard") + } + Action { + id: copyPath + objectName: "HifiAction_" + MenuConstants.CopyPath + text: qsTr("Copy Path to Clipboard") + } + Action { + id: decreaseAvatarSize + objectName: "HifiAction_" + MenuConstants.DecreaseAvatarSize + text: qsTr("Decrease Avatar Size") + } + Action { + id: deleteBookmark + objectName: "HifiAction_" + MenuConstants.DeleteBookmark + text: qsTr("Delete Bookmark...") + } + Action { + id: disableActivityLogger + objectName: "HifiAction_" + MenuConstants.DisableActivityLogger + text: qsTr("Disable Activity Logger") + } + Action { + id: disableLightEntities + objectName: "HifiAction_" + MenuConstants.DisableLightEntities + text: qsTr("Disable Light Entities") + } + Action { + id: disableNackPackets + objectName: "HifiAction_" + MenuConstants.DisableNackPackets + text: qsTr("Disable NACK Packets") + } + Action { + id: diskCacheEditor + objectName: "HifiAction_" + MenuConstants.DiskCacheEditor + text: qsTr("Disk Cache Editor") + } + Action { + id: displayHands + objectName: "HifiAction_" + MenuConstants.DisplayHands + text: qsTr("Show Hand Info") + } + Action { + id: displayHandTargets + objectName: "HifiAction_" + MenuConstants.DisplayHandTargets + text: qsTr("Show Hand Targets") + } + Action { + id: displayModelBounds + objectName: "HifiAction_" + MenuConstants.DisplayModelBounds + text: qsTr("Display Model Bounds") + } + Action { + id: displayModelTriangles + objectName: "HifiAction_" + MenuConstants.DisplayModelTriangles + text: qsTr("Display Model Triangles") + } + Action { + id: displayModelElementChildProxies + objectName: "HifiAction_" + MenuConstants.DisplayModelElementChildProxies + text: qsTr("Display Model Element Children") + } + Action { + id: displayModelElementProxy + objectName: "HifiAction_" + MenuConstants.DisplayModelElementProxy + text: qsTr("Display Model Element Bounds") + } + Action { + id: displayDebugTimingDetails + objectName: "HifiAction_" + MenuConstants.DisplayDebugTimingDetails + text: qsTr("Display Timing Details") + } + Action { + id: dontDoPrecisionPicking + objectName: "HifiAction_" + MenuConstants.DontDoPrecisionPicking + text: qsTr("Don't Do Precision Picking") + } + Action { + id: dontFadeOnOctreeServerChanges + objectName: "HifiAction_" + MenuConstants.DontFadeOnOctreeServerChanges + text: qsTr("Don't Fade In/Out on Octree Server Changes") + } + Action { + id: dontRenderEntitiesAsScene + objectName: "HifiAction_" + MenuConstants.DontRenderEntitiesAsScene + text: qsTr("Don't Render Entities as Scene") + } + Action { + id: echoLocalAudio + objectName: "HifiAction_" + MenuConstants.EchoLocalAudio + text: qsTr("Echo Local Audio") + } + Action { + id: echoServerAudio + objectName: "HifiAction_" + MenuConstants.EchoServerAudio + text: qsTr("Echo Server Audio") + } + Action { + id: editEntitiesHelp + objectName: "HifiAction_" + MenuConstants.EditEntitiesHelp + text: qsTr("Edit Entities Help...") + } + Action { + id: enable3DTVMode + objectName: "HifiAction_" + MenuConstants.Enable3DTVMode + text: qsTr("Enable 3DTV Mode") + } + Action { + id: enableCharacterController + objectName: "HifiAction_" + MenuConstants.EnableCharacterController + text: qsTr("Enable avatar collisions") + } + Action { + id: enableGlowEffect + objectName: "HifiAction_" + MenuConstants.EnableGlowEffect + text: qsTr("Enable Glow Effect (Warning: Poor Oculus Performance)") + } + Action { + id: enableVRMode + objectName: "HifiAction_" + MenuConstants.EnableVRMode + text: qsTr("Enable VR Mode") + } + Action { + id: expandMyAvatarSimulateTiming + objectName: "HifiAction_" + MenuConstants.ExpandMyAvatarSimulateTiming + text: qsTr("Expand /myAvatar/simulation") + } + Action { + id: expandMyAvatarTiming + objectName: "HifiAction_" + MenuConstants.ExpandMyAvatarTiming + text: qsTr("Expand /myAvatar") + } + Action { + id: expandOtherAvatarTiming + objectName: "HifiAction_" + MenuConstants.ExpandOtherAvatarTiming + text: qsTr("Expand /otherAvatar") + } + Action { + id: expandPaintGLTiming + objectName: "HifiAction_" + MenuConstants.ExpandPaintGLTiming + text: qsTr("Expand /paintGL") + } + Action { + id: expandUpdateTiming + objectName: "HifiAction_" + MenuConstants.ExpandUpdateTiming + text: qsTr("Expand /update") + } + Action { + id: faceshift + objectName: "HifiAction_" + MenuConstants.Faceshift + text: qsTr("Faceshift") + } + Action { + id: filterSixense + objectName: "HifiAction_" + MenuConstants.FilterSixense + text: qsTr("Smooth Sixense Movement") + } + Action { + id: firstPerson + objectName: "HifiAction_" + MenuConstants.FirstPerson + text: qsTr("First Person") + } + Action { + id: frameTimer + objectName: "HifiAction_" + MenuConstants.FrameTimer + text: qsTr("Show Timer") + } + Action { + id: fullscreen + objectName: "HifiAction_" + MenuConstants.Fullscreen + text: qsTr("Fullscreen") + } + Action { + id: fullscreenMirror + objectName: "HifiAction_" + MenuConstants.FullscreenMirror + text: qsTr("Fullscreen Mirror") + } + Action { + id: glowWhenSpeaking + objectName: "HifiAction_" + MenuConstants.GlowWhenSpeaking + text: qsTr("Glow When Speaking") + } + Action { + id: namesAboveHeads + objectName: "HifiAction_" + MenuConstants.NamesAboveHeads + text: qsTr("Names Above Heads") + } + Action { + id: goToUser + objectName: "HifiAction_" + MenuConstants.GoToUser + text: qsTr("Go To User") + } + Action { + id: hMDTools + objectName: "HifiAction_" + MenuConstants.HMDTools + text: qsTr("HMD Tools") + } + Action { + id: increaseAvatarSize + objectName: "HifiAction_" + MenuConstants.IncreaseAvatarSize + text: qsTr("Increase Avatar Size") + } + Action { + id: keyboardMotorControl + objectName: "HifiAction_" + MenuConstants.KeyboardMotorControl + text: qsTr("Enable Keyboard Motor Control") + } + Action { + id: leapMotionOnHMD + objectName: "HifiAction_" + MenuConstants.LeapMotionOnHMD + text: qsTr("Leap Motion on HMD") + } + Action { + id: loadRSSDKFile + objectName: "HifiAction_" + MenuConstants.LoadRSSDKFile + text: qsTr("Load .rssdk file") + } + Action { + id: lodTools + objectName: "HifiAction_" + MenuConstants.LodTools + text: qsTr("LOD Tools") + } + Action { + id: log + objectName: "HifiAction_" + MenuConstants.Log + text: qsTr("Log") + } + Action { + id: lowVelocityFilter + objectName: "HifiAction_" + MenuConstants.LowVelocityFilter + text: qsTr("Low Velocity Filter") + } + Action { + id: mirror + objectName: "HifiAction_" + MenuConstants.Mirror + text: qsTr("Mirror") + } + Action { + id: muteAudio + objectName: "HifiAction_" + MenuConstants.MuteAudio + text: qsTr("Mute Microphone") + } + Action { + id: muteEnvironment + objectName: "HifiAction_" + MenuConstants.MuteEnvironment + text: qsTr("Mute Environment") + } + Action { + id: noFaceTracking + objectName: "HifiAction_" + MenuConstants.NoFaceTracking + text: qsTr("None") + } + Action { + id: noShadows + objectName: "HifiAction_" + MenuConstants.NoShadows + text: qsTr("None") + } + Action { + id: octreeStats + objectName: "HifiAction_" + MenuConstants.OctreeStats + text: qsTr("Entity Statistics") + } + Action { + id: offAxisProjection + objectName: "HifiAction_" + MenuConstants.OffAxisProjection + text: qsTr("Off-Axis Projection") + } + Action { + id: onlyDisplayTopTen + objectName: "HifiAction_" + MenuConstants.OnlyDisplayTopTen + text: qsTr("Only Display Top Ten") + } + Action { + id: packageModel + objectName: "HifiAction_" + MenuConstants.PackageModel + text: qsTr("Package Model...") + } + Action { + id: pair + objectName: "HifiAction_" + MenuConstants.Pair + text: qsTr("Pair") + } + Action { + id: pipelineWarnings + objectName: "HifiAction_" + MenuConstants.PipelineWarnings + text: qsTr("Log Render Pipeline Warnings") + } + Action { + id: preferences + objectName: "HifiAction_" + MenuConstants.Preferences + text: qsTr("Preferences...") + } + Action { + id: renderBoundingCollisionShapes + objectName: "HifiAction_" + MenuConstants.RenderBoundingCollisionShapes + text: qsTr("Show Bounding Collision Shapes") + checkable: true + } + Action { + id: renderFocusIndicator + objectName: "HifiAction_" + MenuConstants.RenderFocusIndicator + text: qsTr("Show Eye Focus") + checkable: true + } + Action { + id: renderHeadCollisionShapes + objectName: "HifiAction_" + MenuConstants.RenderHeadCollisionShapes + text: qsTr("Show Head Collision Shapes") + checkable: true + } + Action { + id: renderLookAtVectors + objectName: "HifiAction_" + MenuConstants.RenderLookAtVectors + text: qsTr("Show Look-at Vectors") + checkable: true + } + Action { + id: renderSkeletonCollisionShapes + objectName: "HifiAction_" + MenuConstants.RenderSkeletonCollisionShapes + text: qsTr("Show Skeleton Collision Shapes") + checkable: true + } + ExclusiveGroup { + Action { + id: renderTargetFramerateUnlimited + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerateUnlimited + text: qsTr("Unlimited") + checked: true + checkable: true + } + Action { + id: renderTargetFramerate60 + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate60 + text: qsTr("60") + checkable: true + } + Action { + id: renderTargetFramerate50 + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate50 + text: qsTr("50") + checkable: true + } + Action { + id: renderTargetFramerate40 + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate40 + text: qsTr("40") + checkable: true + } + Action { + id: renderTargetFramerate30 + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate30 + text: qsTr("30") + checkable: true + } + } + Action { + id: renderTargetFramerateVSyncOn + objectName: "HifiAction_" + MenuConstants.RenderTargetFramerateVSyncOn + text: qsTr("V-Sync On") + checkable: true + } + Action { + id: renderResolution + objectName: "HifiAction_" + MenuConstants.RenderResolution + text: qsTr("Scale Resolution") + } + ExclusiveGroup { + Action { + id: renderResolutionOne + objectName: "HifiAction_" + MenuConstants.RenderResolutionOne + text: qsTr("1") + checkable: true + checked: true + } + Action { + id: renderResolutionTwoThird + objectName: "HifiAction_" + MenuConstants.RenderResolutionTwoThird + text: qsTr("2/3") + checkable: true + } + Action { + id: renderResolutionHalf + objectName: "HifiAction_" + MenuConstants.RenderResolutionHalf + text: qsTr("1/2") + checkable: true + } + Action { + id: renderResolutionThird + objectName: "HifiAction_" + MenuConstants.RenderResolutionThird + text: qsTr("1/3") + checkable: true + } + Action { + id: renderResolutionQuarter + objectName: "HifiAction_" + MenuConstants.RenderResolutionQuarter + text: qsTr("1/4") + checkable: true + } + } + Action { + id: renderAmbientLight + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight + text: qsTr("Ambient Light") + } + ExclusiveGroup { + Action { + id: renderAmbientLightGlobal + objectName: "HifiAction_" + MenuConstants.RenderAmbientLightGlobal + text: qsTr("Global") + checked: true + checkable: true + } + Action { + id: renderAmbientLight0 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight0 + text: qsTr("OLD_TOWN_SQUARE") + checkable: true + } + Action { + id: renderAmbientLight1 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight1 + text: qsTr("GRACE_CATHEDRAL") + checkable: true + } + Action { + id: renderAmbientLight2 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight2 + text: qsTr("EUCALYPTUS_GROVE") + checkable: true + } + Action { + id: renderAmbientLight3 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight3 + text: qsTr("ST_PETERS_BASILICA") + checkable: true + } + Action { + id: renderAmbientLight4 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight4 + text: qsTr("UFFIZI_GALLERY") + checkable: true + } + Action { + id: renderAmbientLight5 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight5 + text: qsTr("GALILEOS_TOMB") + checkable: true + } + Action { + id: renderAmbientLight6 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight6 + text: qsTr("VINE_STREET_KITCHEN") + checkable: true + } + Action { + id: renderAmbientLight7 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight7 + text: qsTr("BREEZEWAY") + checkable: true + } + Action { + id: renderAmbientLight8 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight8 + text: qsTr("CAMPUS_SUNSET") + checkable: true + } + Action { + id: renderAmbientLight9 + objectName: "HifiAction_" + MenuConstants.RenderAmbientLight9 + text: qsTr("FUNSTON_BEACH_SUNSET") + checkable: true + } + } + Action { + id: resetAvatarSize + objectName: "HifiAction_" + MenuConstants.ResetAvatarSize + text: qsTr("Reset Avatar Size") + } + Action { + id: runTimingTests + objectName: "HifiAction_" + MenuConstants.RunTimingTests + text: qsTr("Run Timing Tests") + } + Action { + id: scriptedMotorControl + objectName: "HifiAction_" + MenuConstants.ScriptedMotorControl + text: qsTr("Enable Scripted Motor Control") + } + Action { + id: showBordersEntityNodes + objectName: "HifiAction_" + MenuConstants.ShowBordersEntityNodes + text: qsTr("Show Entity Nodes") + } + Action { + id: showIKConstraints + objectName: "HifiAction_" + MenuConstants.ShowIKConstraints + text: qsTr("Show IK Constraints") + } + Action { + id: simpleShadows + objectName: "HifiAction_" + MenuConstants.SimpleShadows + text: qsTr("Simple") + } + Action { + id: sixenseEnabled + objectName: "HifiAction_" + MenuConstants.SixenseEnabled + text: qsTr("Enable Hydra Support") + } + Action { + id: sixenseMouseInput + objectName: "HifiAction_" + MenuConstants.SixenseMouseInput + text: qsTr("Enable Sixense Mouse Input") + } + Action { + id: sixenseLasers + objectName: "HifiAction_" + MenuConstants.SixenseLasers + text: qsTr("Enable Sixense UI Lasers") + } + Action { + id: shiftHipsForIdleAnimations + objectName: "HifiAction_" + MenuConstants.ShiftHipsForIdleAnimations + text: qsTr("Shift hips for idle animations") + } + Action { + id: stars + objectName: "HifiAction_" + MenuConstants.Stars + text: qsTr("Stars") + } + Action { + id: stats + objectName: "HifiAction_" + MenuConstants.Stats + text: qsTr("Stats") + } + Action { + id: stereoAudio + objectName: "HifiAction_" + MenuConstants.StereoAudio + text: qsTr("Stereo Audio (disables spatial sound)") + } + Action { + id: suppressShortTimings + objectName: "HifiAction_" + MenuConstants.SuppressShortTimings + text: qsTr("Suppress Timings Less than 10ms") + } + Action { + id: testPing + objectName: "HifiAction_" + MenuConstants.TestPing + text: qsTr("Test Ping") + } + Action { + id: transmitterDrive + objectName: "HifiAction_" + MenuConstants.TransmitterDrive + text: qsTr("Transmitter Drive") + } + Action { + id: turnWithHead + objectName: "HifiAction_" + MenuConstants.TurnWithHead + text: qsTr("Turn using Head") + } + Action { + id: useAudioForMouth + objectName: "HifiAction_" + MenuConstants.UseAudioForMouth + text: qsTr("Use Audio for Mouth") + } + Action { + id: useCamera + objectName: "HifiAction_" + MenuConstants.UseCamera + text: qsTr("Use Camera") + } + Action { + id: velocityFilter + objectName: "HifiAction_" + MenuConstants.VelocityFilter + text: qsTr("Velocity Filter") + } + Action { + id: wireframe + objectName: "HifiAction_" + MenuConstants.Wireframe + text: qsTr("Wireframe") + } + } + + MenuBar { + objectName: "rootMenu"; + Menu { + title: "File" + MenuItem { + action: login + } + MenuItem { + action: aboutApp + } + MenuSeparator {} MenuItem { text: "Scripts"; enabled: false } + MenuItem { action: loadScript; } + MenuItem { action: loadScriptURL; } + MenuItem { action: stopAllScripts; } + MenuItem { action: reloadAllScripts; } + MenuItem { action: runningScripts; } + MenuSeparator {} MenuItem { text: "Locations"; enabled: false } + MenuItem { action: addressBar; } + MenuItem { action: copyAddress; } + MenuItem { action: copyPath; } + MenuSeparator {} + MenuItem { action: quit; } + } + Menu { + title: "Edit" + MenuItem { action: preferences; } + MenuItem { action: animations; } + } + Menu { + title: "Tools" + MenuItem { action: scriptEditor; } + MenuItem { action: controlWithSpeech; } + MenuItem { action: chat; } + MenuItem { action: addRemoveFriends; } + Menu { + title: "I Am Visible To" + MenuItem { action: visibleToEveryone; } + MenuItem { action: visibleToFriends; } + MenuItem { action: visibleToNoOne; } + } + MenuItem { action: toolWindow; } + MenuItem { action: javascriptConsole; } + MenuItem { action: resetSensors; } + MenuItem { action: packageModel; } + } + Menu { + title: "Avatar" + Menu { + title: "Size" + MenuItem { action: increaseAvatarSize; } + MenuItem { action: decreaseAvatarSize; } + MenuItem { action: resetAvatarSize; } + } + MenuItem { action: keyboardMotorControl; } + MenuItem { action: scriptedMotorControl; } + MenuItem { action: namesAboveHeads; } + MenuItem { action: glowWhenSpeaking; } + MenuItem { action: blueSpeechSphere; } + MenuItem { action: enableCharacterController; } + MenuItem { action: shiftHipsForIdleAnimations; } + } + Menu { + title: "View" + MenuItem { action: fullscreen; } + MenuItem { action: firstPerson; } + MenuItem { action: mirror; } + MenuItem { action: fullscreenMirror; } + MenuItem { action: hMDTools; } + MenuItem { action: enableVRMode; } + MenuItem { action: enable3DTVMode; } + MenuItem { action: showBordersEntityNodes; } + MenuItem { action: offAxisProjection; } + MenuItem { action: turnWithHead; } + MenuItem { action: stats; } + MenuItem { action: log; } + MenuItem { action: bandwidthDetails; } + MenuItem { action: octreeStats; } + } + Menu { + title: "Developer" + Menu { + title: "Render" + MenuItem { action: atmosphere; } + MenuItem { action: ambientOcclusion; } + MenuItem { action: dontFadeOnOctreeServerChanges; } + Menu { + title: "Ambient Light" + MenuItem { action: renderAmbientLightGlobal; } + MenuItem { action: renderAmbientLight0; } + MenuItem { action: renderAmbientLight1; } + MenuItem { action: renderAmbientLight2; } + MenuItem { action: renderAmbientLight3; } + MenuItem { action: renderAmbientLight4; } + MenuItem { action: renderAmbientLight5; } + MenuItem { action: renderAmbientLight6; } + MenuItem { action: renderAmbientLight7; } + MenuItem { action: renderAmbientLight8; } + MenuItem { action: renderAmbientLight9; } + } + Menu { + title: "Shadows" + MenuItem { action: noShadows; } + MenuItem { action: simpleShadows; } + MenuItem { action: cascadedShadows; } + } + Menu { + title: "Framerate" + MenuItem { action: renderTargetFramerateUnlimited; } + MenuItem { action: renderTargetFramerate60; } + MenuItem { action: renderTargetFramerate50; } + MenuItem { action: renderTargetFramerate40; } + MenuItem { action: renderTargetFramerate30; } + } + MenuItem { action: renderTargetFramerateVSyncOn; } + Menu { + title: "Scale Resolution" + MenuItem { action: renderResolutionOne; } + MenuItem { action: renderResolutionTwoThird; } + MenuItem { action: renderResolutionHalf; } + MenuItem { action: renderResolutionThird; } + MenuItem { action: renderResolutionQuarter; } + } + MenuItem { action: stars; } + MenuItem { action: enableGlowEffect; } + MenuItem { action: wireframe; } + MenuItem { action: lodTools; } + } + Menu { + title: "Avatar" + Menu { + title: "Face Tracking" + MenuItem { action: noFaceTracking; } + MenuItem { action: faceshift; } + MenuItem { action: useCamera; } + MenuItem { action: useAudioForMouth; } + MenuItem { action: velocityFilter; } + } + MenuItem { action: renderSkeletonCollisionShapes; } + MenuItem { action: renderHeadCollisionShapes; } + MenuItem { action: renderBoundingCollisionShapes; } + MenuItem { action: renderLookAtVectors; } + MenuItem { action: renderFocusIndicator; } + } + Menu { + title: "Hands" + MenuItem { action: alignForearmsWithWrists; } + MenuItem { action: alternateIK; } + MenuItem { action: displayHands; } + MenuItem { action: displayHandTargets; } + MenuItem { action: showIKConstraints; } + Menu { + title: "Sixense" + MenuItem { action: sixenseEnabled; } + MenuItem { action: filterSixense; } + MenuItem { action: lowVelocityFilter; } + MenuItem { action: sixenseMouseInput; } + MenuItem { action: sixenseLasers; } + } + Menu { + title: "Leap Motion" + MenuItem { action: leapMotionOnHMD; } + } + MenuItem { action: loadRSSDKFile; } + } + Menu { + title: "Network" + MenuItem { action: disableNackPackets; } + MenuItem { action: disableActivityLogger; } + MenuItem { action: cachesSize; } + MenuItem { action: diskCacheEditor; } + } + Menu { + title: "Timing and Stats" + Menu { + title: "Performance Timer" + MenuItem { action: displayDebugTimingDetails; } + MenuItem { action: onlyDisplayTopTen; } + MenuItem { action: expandUpdateTiming; } + MenuItem { action: expandMyAvatarTiming; } + MenuItem { action: expandMyAvatarSimulateTiming; } + MenuItem { action: expandOtherAvatarTiming; } + MenuItem { action: expandPaintGLTiming; } + } + MenuItem { action: testPing; } + MenuItem { action: frameTimer; } + MenuItem { action: runTimingTests; } + MenuItem { action: pipelineWarnings; } + MenuItem { action: suppressShortTimings; } + } + Menu { + title: "Audio" + MenuItem { action: audioNoiseReduction; } + MenuItem { action: echoServerAudio; } + MenuItem { action: echoLocalAudio; } + MenuItem { action: stereoAudio; } + MenuItem { action: muteAudio; } + MenuItem { action: muteEnvironment; } + Menu { + title: "Audio Scope" + MenuItem { action: audioScope; } + MenuItem { action: audioScopePause; } + MenuSeparator {} MenuItem { text: "Audio Scope"; enabled: false; } + MenuItem { action: audioScopeFiveFrames; } + MenuItem { action: audioScopeTwentyFrames; } + MenuItem { action: audioScopeFiftyFrames; } + } + MenuItem { action: audioStats; } + MenuItem { action: audioStatsShowInjectedStreams; } + } + } + Menu { + title: "Help" + MenuItem { action: editEntitiesHelp; } + MenuItem { action: aboutApp; } + } + } +} diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1f22cf3550..b22bacef33 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -761,17 +761,6 @@ void Application::initializeGL() { InfoView::showFirstTime(INFO_HELP_PATH); } -class OAuthFactory : public QQmlNetworkAccessManagerFactory { - QThreadStorage oauthNetworkAccessManagers; -public: - virtual QNetworkAccessManager * create(QObject * parent) { - if (!oauthNetworkAccessManagers.hasLocalData()) { - oauthNetworkAccessManagers.setLocalData(OAuthNetworkAccessManager::getInstance()); - } - return oauthNetworkAccessManagers.localData(); - } -}; - void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 56fa7a0aef..5922d9f3bb 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -35,8 +35,8 @@ void LoginDialog::toggleAction() { }); } else { // change the menu item to login - loginAction->setText("Login"); - connect(loginAction, &QAction::triggered, [] { + menu->setOptionText(MenuOption::Login, "Login"); + menu->setOptionTriggerAction(MenuOption::Login, [] { LoginDialog::show(); }); } diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp index 4893d47103..f37f71ef54 100644 --- a/interface/src/ui/MarketplaceDialog.cpp +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -12,7 +12,7 @@ #include "MarketplaceDialog.h" #include "DependencyManager.h" -QML_DIALOG_DEF(MarketplaceDialog) +HIFI_QML_DEF(MarketplaceDialog) MarketplaceDialog::MarketplaceDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { diff --git a/interface/src/ui/MarketplaceDialog.h b/interface/src/ui/MarketplaceDialog.h index f5fa355201..241d4a7131 100644 --- a/interface/src/ui/MarketplaceDialog.h +++ b/interface/src/ui/MarketplaceDialog.h @@ -17,7 +17,7 @@ class MarketplaceDialog : public OffscreenQmlDialog { Q_OBJECT - QML_DIALOG_DECL + HIFI_QML_DECL public: MarketplaceDialog(QQuickItem *parent = 0); diff --git a/interface/src/ui/MenuQml.cpp b/interface/src/ui/MenuQml.cpp deleted file mode 100644 index 991b7e4574..0000000000 --- a/interface/src/ui/MenuQml.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// MenuQml.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 "MenuQml.h" - -QML_DIALOG_DEF(MenuQml) - -MenuQml::MenuQml(QQuickItem *parent) : QQuickItem(parent) { - auto menu = Menu::getInstance(); - -} diff --git a/interface/src/ui/MenuQml.h b/interface/src/ui/MenuQml.h deleted file mode 100644 index a007ec8735..0000000000 --- a/interface/src/ui/MenuQml.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// MenuQml.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_MenuQml_h -#define hifi_MenuQml_h - -#include - -class MenuQml : public QQuickItem -{ - Q_OBJECT - QML_DIALOG_DECL - -public: - MenuQml(QQuickItem *parent = 0); -}; - -#endif diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp index 27517f99d7..88c9b48f43 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/HifiMenu.cpp @@ -277,3 +277,26 @@ void HifiMenu::connectItem(const QString& menuOption, QObject* receiver, const c QObject* result = findMenuObject(menuOption); connect(result, SIGNAL(triggered()), receiver, slot); } + +void HifiMenu::setExclusiveGroup(const QString& menuOption, const QString& groupName) { + static const QString GROUP_SUFFIX{ "__Group" }; + auto offscreenUi = DependencyManager::get(); + QObject* group = offscreenUi->getRootItem()->findChild(groupName + GROUP_SUFFIX); + if (!group) { + group = offscreenUi->load("ExclusiveGroup.qml"); + Q_ASSERT(group); + } + QObject* menuItem = findMenuObject(menuOption); + bool result = menuItem->setProperty("text", QVariant::fromValue(group)); + Q_ASSERT(result); +} + +bool HifiMenu::connectAction(int action, QObject * receiver, const char * slot) { + auto offscreenUi = DependencyManager::get(); + QObject* rootMenu = offscreenUi->getRootItem()->findChild("AllActions"); + QString name = "HifiAction_" + QVariant(action).toString(); + QObject* quitAction = rootMenu->findChild(name); + connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); + return true; +} + diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h index c89c91b028..501baa3cc0 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -23,6 +23,9 @@ class HifiMenu : public QQuickItem { HIFI_QML_DECL_LAMBDA public: + + static bool connectAction(int action, QObject * receiver, const char * slot); + HifiMenu(QQuickItem* parent = nullptr); void setToggleAction(const QString& name, std::function f); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 76a5e71b25..052a6aac4e 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -152,13 +152,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; } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index b58b4e59cb..ce40bec943 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -38,9 +38,9 @@ private: \ static const QUrl QML; \ public: \ static void registerType(); \ - static void show(std::function f = [](QQmlContext*, QQuickItem*) {}); \ - static void toggle(std::function f = [](QQmlContext*, QQuickItem*) {}); \ - static void load(std::function f = [](QQmlContext*, QQuickItem*) {}); \ + static void show(std::function f = [](QQmlContext*, QObject*) {}); \ + static void toggle(std::function f = [](QQmlContext*, QObject*) {}); \ + static void load(std::function f = [](QQmlContext*, QObject*) {}); \ private: #define HIFI_QML_DECL_LAMBDA \ @@ -62,16 +62,16 @@ private: qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \ } \ \ - void x::show(std::function f) { \ + void x::show(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ offscreenUi->show(QML, NAME, f); \ } \ \ - void x::toggle(std::function f) { \ + void x::toggle(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ offscreenUi->toggle(QML, NAME, f); \ } \ - void x::load(std::function f) { \ + void x::load(std::function f) { \ auto offscreenUi = DependencyManager::get(); \ offscreenUi->load(QML, f); \ } @@ -122,9 +122,9 @@ public: virtual ~OffscreenUi(); void create(QOpenGLContext* context); void resize(const QSize& size); - void load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); - void load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { - load(QUrl(qmlSourceFile), f); + QObject* load(const QUrl& qmlSource, std::function f = [](QQmlContext*, QObject*) {}); + QObject* load(const QString& qmlSourceFile, std::function f = [](QQmlContext*, QObject*) {}) { + return load(QUrl(qmlSourceFile), f); } void show(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); void toggle(const QUrl& url, const QString& name, std::function f = [](QQmlContext*, QObject*) {}); @@ -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(); - void finishQmlLoad(std::function f); public slots: void requestUpdate(); diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 6af87fec2e..37e229d571 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -74,6 +74,171 @@ public: }; +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()) { @@ -156,6 +321,8 @@ public: MessageDialog::registerType(); HifiMenu::registerType(); + qmlRegisterType("Hifi", 1, 0, "MenuConstants"); + auto offscreenUi = DependencyManager::get(); offscreenUi->create(_context); @@ -181,24 +348,10 @@ public: #else offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->load(QUrl("RootMenu.qml")); + offscreenUi->load(QUrl("Menu.qml")); + // Requires a root menu to have been loaded before it can load 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..."); + HifiMenu::connectAction(MenuConstants::Quit, qApp, SLOT(quit())); #endif installEventFilter(offscreenUi.data()); offscreenUi->resume(); From a96f69a67316bf245b49cec5dc7b5b54fc973f1a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 25 Apr 2015 16:20:15 -0700 Subject: [PATCH 14/33] Switching to a wrapper mechanism for VR menus --- interface/resources/qml/AddressBarDialog.qml | 2 +- .../qml/{Menu.qml => InterfaceMenu.qml} | 17 +- interface/resources/qml/Main.qml | 20 - interface/resources/qml/MessageDialog.qml | 7 +- interface/resources/qml/Root.qml | 2 +- interface/resources/qml/TestRoot.qml | 7 +- .../qml/{HifiMenu.qml => VrMenu.qml} | 40 +- interface/resources/qml/controls/Dialog.qml | 2 +- interface/resources/qml/images/critical.png | Bin 253 -> 0 bytes .../resources/qml/images/information.png | Bin 254 -> 0 bytes interface/resources/qml/images/question.png | Bin 257 -> 0 bytes interface/resources/qml/images/warning.png | Bin 224 -> 0 bytes interface/src/Application.cpp | 96 +- interface/src/Application.h | 3 + interface/src/Bookmarks.cpp | 67 +- interface/src/Bookmarks.h | 14 +- interface/src/MainWindow.cpp | 6 +- interface/src/Menu.cpp | 1423 ++++++++++------- interface/src/Menu.h | 144 +- interface/src/devices/OculusManager.cpp | 2 +- .../src/scripting/MenuScriptingInterface.cpp | 48 +- .../scripting/WindowScriptingInterface.cpp | 1 + interface/src/ui/HMDToolsDialog.cpp | 1 - interface/src/ui/LoginDialog.cpp | 17 +- interface/src/ui/RunningScriptsWidget.cpp | 9 +- interface/src/ui/ToolWindow.cpp | 3 +- libraries/ui/src/HifiMenu.cpp | 366 ++--- libraries/ui/src/HifiMenu.h | 63 +- libraries/ui/src/OffscreenUi.cpp | 7 +- tests/ui/src/main.cpp | 13 +- 30 files changed, 1335 insertions(+), 1045 deletions(-) rename interface/resources/qml/{Menu.qml => InterfaceMenu.qml} (99%) delete mode 100644 interface/resources/qml/Main.qml rename interface/resources/qml/{HifiMenu.qml => VrMenu.qml} (87%) delete mode 100644 interface/resources/qml/images/critical.png delete mode 100644 interface/resources/qml/images/information.png delete mode 100644 interface/resources/qml/images/question.png delete mode 100644 interface/resources/qml/images/warning.png diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8a2bc3e2b4..8d2439e5b8 100644 --- a/interface/resources/qml/AddressBarDialog.qml +++ b/interface/resources/qml/AddressBarDialog.qml @@ -48,7 +48,7 @@ Dialog { helperText: "domain, location, @user, /x,y,z" anchors.margins: 8 onAccepted: { - event.accepted + event.accepted addressBarDialog.loadAddress(addressLine.text) } } diff --git a/interface/resources/qml/Menu.qml b/interface/resources/qml/InterfaceMenu.qml similarity index 99% rename from interface/resources/qml/Menu.qml rename to interface/resources/qml/InterfaceMenu.qml index 3939794fb2..2a5ed8d212 100644 --- a/interface/resources/qml/Menu.qml +++ b/interface/resources/qml/InterfaceMenu.qml @@ -2,6 +2,7 @@ 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" @@ -10,7 +11,7 @@ Item { objectName: "HifiAction_" + MenuConstants.AboutApp text: qsTr("About Interface") } - + // // File Menu // @@ -26,7 +27,7 @@ Item { //shortcut: StandardKey.Quit shortcut: "Ctrl+Q" } - + // Scripts Action { id: loadScript @@ -58,7 +59,7 @@ Item { objectName: "HifiAction_" + MenuConstants.StopAllScripts text: qsTr("Stop All Scripts") } - + // Locations Action { id: bookmarkLocation @@ -75,7 +76,7 @@ Item { objectName: "HifiAction_" + MenuConstants.AddressBar text: qsTr("Show Address Bar") } - + // // Edit menu // @@ -84,13 +85,13 @@ Item { text: "Undo" shortcut: StandardKey.Undo } - + Action { id: redo text: "Redo" shortcut: StandardKey.Redo } - + Action { id: animations objectName: "HifiAction_" + MenuConstants.Animations @@ -157,7 +158,7 @@ Item { objectName: "HifiAction_" + MenuConstants.ResetSensors text: qsTr("Reset Sensors") } - + @@ -853,7 +854,7 @@ Item { } } - MenuBar { + Menu { objectName: "rootMenu"; Menu { title: "File" diff --git a/interface/resources/qml/Main.qml b/interface/resources/qml/Main.qml deleted file mode 100644 index 8911a4c9f4..0000000000 --- a/interface/resources/qml/Main.qml +++ /dev/null @@ -1,20 +0,0 @@ -import QtQuick 2.4 -import QtQuick.Controls 1.3 - -ApplicationWindow { - id: root - width: 800 - height: 600 - visible: true - - menuBar: MenuBar { - Menu { - title: "File" - MenuItem { - text: "Quit" - } - } - } - -} - diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index 3a1c92cdf3..f469e33c30 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -9,6 +9,7 @@ Dialog { property real spacing: 8 property real outerSpacing: 16 + destroyOnCloseButton: true destroyOnInvisible: true implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 @@ -35,7 +36,7 @@ Dialog { onEnabledChanged: { if (enabled) { - root.forceActiveFocus(); + root.forceActiveFocus(); } } @@ -334,7 +335,7 @@ Dialog { case Qt.Key_Period: if (Qt.platform.os === "osx") { event.accepted = true - content.reject() + content.reject() } break } else switch (event.key) { @@ -346,7 +347,7 @@ Dialog { case Qt.Key_Enter: case Qt.Key_Return: - console.log("Accepting"); + 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/TestRoot.qml b/interface/resources/qml/TestRoot.qml index 0b9209aa76..bd38c696bf 100644 --- a/interface/resources/qml/TestRoot.qml +++ b/interface/resources/qml/TestRoot.qml @@ -6,12 +6,11 @@ import QtQuick.Controls 1.3 import "controls" Root { - id: root + id: root anchors.fill: parent onParentChanged: { - forceActiveFocus(); + forceActiveFocus(); } - Button { id: messageBox anchors.right: createDialog.left @@ -38,7 +37,7 @@ Root { } Keys.onPressed: { - console.log(event.key); + console.log("Key press root") } } diff --git a/interface/resources/qml/HifiMenu.qml b/interface/resources/qml/VrMenu.qml similarity index 87% rename from interface/resources/qml/HifiMenu.qml rename to interface/resources/qml/VrMenu.qml index 2c6963a349..8c4aaafb3b 100644 --- a/interface/resources/qml/HifiMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -5,10 +5,10 @@ import QtQuick.Controls.Styles 1.3 import "controls" import "styles" -Hifi.HifiMenu { +Hifi.VrMenu { id: root anchors.fill: parent - objectName: "HifiMenu" + objectName: "VrMenu" enabled: false opacity: 0.0 property int animationDuration: 200 @@ -17,7 +17,7 @@ Hifi.HifiMenu { onEnabledChanged: { if (enabled && columns.length == 0) { - pushColumn(rootMenu.menus); + pushColumn(rootMenu.items); } opacity = enabled ? 1.0 : 0.0 if (enabled) { @@ -47,13 +47,21 @@ Hifi.HifiMenu { property var menuBuilder: Component { Border { + 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; + } + } 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 + property int menuDepth ListView { spacing: 6 @@ -186,7 +194,25 @@ Hifi.HifiMenu { anchors.top: parent.top anchors.topMargin: 0 width: listView.width + hoverEnabled: true + Timer { + id: timer + interval: 1000 + onTriggered: parent.select(); + } + onEntered: { + if (source.type == 2 && enabled) { + timer.start() + } + } + onExited: { + timer.stop() + } onClicked: { + select(); + } + function select() { + timer.stop(); listView.currentIndex = listViewIndex parent.root.selectItem(parent.source); } diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 07162ad1d8..d722d5264a 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -17,7 +17,7 @@ Item { HifiPalette { id: hifiPalette } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } x: parent ? parent.width / 2 - width / 2 : 0 - y: parent ? parent.height / 2 - height / 2 : 0 + y: parent ? parent.height / 2 - height / 2 : 0 property int animationDuration: 400 property bool destroyOnInvisible: false diff --git a/interface/resources/qml/images/critical.png b/interface/resources/qml/images/critical.png deleted file mode 100644 index dc9c5aebf453dd92dba60c48f060b34f0087e02c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 253 zcmVkNDWE^AyB8@ZEC00000NkvXXu0mjf DKfYr$ diff --git a/interface/resources/qml/images/information.png b/interface/resources/qml/images/information.png deleted file mode 100644 index 0a2eb87d108d2a24b71559998627570a252ebe69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 254 zcmeAS@N?(olHy`uVBq!ia0y~yU{C;I7G?$phQ^Te;|vT8`~f~8uBj!u3?T4-=FFM@ z|NrludHNay0|R48kY6x^!?PP{3=9l&JzX3_G|sP`c#-$80*{Mh+yV`sgwlqP>#QC( z>X#+u7`Xm@Q`=BsWqr<)MUc^=nfrazYu@_ss|q3QYrOu5+ux47bKp#oZChf4k#&aW z_6qNdiK4+zLkc4MIa@Ri#52@3K4zcMoZ;%Na4X>suaM`rgBP8<|7A}r*;L-N)XQ^~ z*Qd}2Q;+ng3Z7t^78=O>`qh(QX^u+vLUu-L1y=d3FMU5VRZj!?kipZ{&t;ucLK6Te CT3=!S diff --git a/interface/resources/qml/images/question.png b/interface/resources/qml/images/question.png deleted file mode 100644 index 2dd92fd7915a09de670b8b6022ddcf02d4cc90e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 257 zcmV+c0sj7pP)NklQ}7)nBez2^$+jK{Nw%L3xK4m{g6jm}2|@>7z7zk+b}w*O&>f9X*ioevByCmJtfv0xgg* zX&;ac>>nrw_75mp9NLlK=){M}g!K&|3b4W-F*J23VSVK);JN=%IJYXN$un1x$~;9b a#PS7=(1SoC6O>K>0000a diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b22bacef33..a9bbaf7de0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -52,8 +52,6 @@ #include #include #include -#include -#include #include #include @@ -65,6 +63,7 @@ #include #include #include +#include #include #include #include @@ -135,7 +134,6 @@ #include "ui/DialogsManager.h" #include "ui/InfoView.h" #include "ui/LoginDialog.h" -#include "ui/MarketplaceDialog.h" #include "ui/Snapshot.h" #include "ui/StandAloneJSConsole.h" #include "ui/Stats.h" @@ -510,6 +508,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // enable mouse tracking; otherwise, we only get drag events _glWidget->setMouseTracking(true); + _fullscreenMenuWidget->setParent(_glWidget); + _menuBarHeight = Menu::getInstance()->height(); if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { setFullscreen(true); // Initialize menu bar show/hide } @@ -730,6 +730,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."); @@ -764,9 +768,7 @@ void Application::initializeGL() { void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); - MarketplaceDialog::registerType(); MessageDialog::registerType(); - Menu::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -902,7 +904,9 @@ void Application::runTests() { } void Application::audioMuteToggled() { - Menu::getInstance()->setIsOptionChecked(MenuOption::MuteAudio, DependencyManager::get()->isMuted()); + QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio); + Q_CHECK_PTR(muteAction); + muteAction->setChecked(DependencyManager::get()->isMuted()); } void Application::aboutApp() { @@ -1067,12 +1071,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) { return false; } -static bool altPressed; -static bool ctrlPressed; +static bool _altPressed{ false }; void Application::keyPressEvent(QKeyEvent* event) { - altPressed = event->key() == Qt::Key_Alt; - ctrlPressed = event->key() == Qt::Key_Control; + _altPressed = event->key() == Qt::Key_Alt; _keysPressed.insert(event->key()); _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts @@ -1162,9 +1164,7 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Backslash: - MarketplaceDialog::show(); - - //Menu::getInstance()->triggerOption(MenuOption::Chat); + Menu::getInstance()->triggerOption(MenuOption::Chat); break; case Qt::Key_Up: @@ -1329,13 +1329,15 @@ void Application::keyPressEvent(QKeyEvent* event) { } void Application::keyReleaseEvent(QKeyEvent* event) { - if (event->key() == Qt::Key_Alt && altPressed && _window->isActiveWindow()) { - Menu::toggle(); + if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) { +#ifndef DEBUG + if (OculusManager::isConnected()) { +#endif + VrMenu::toggle(); +#ifndef DEBUG + } +#endif } - if (event->key() == Qt::Key_Control && ctrlPressed && _window->isActiveWindow()) { - Menu::toggle(); - } - ctrlPressed = altPressed = false; _keysPressed.remove(event->key()); @@ -1346,6 +1348,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) { return; } + switch (event->key()) { case Qt::Key_E: case Qt::Key_PageUp: @@ -1442,6 +1445,18 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { return; } + if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen) + && !Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { + // Show/hide menu bar in fullscreen + if (event->globalY() > _menuBarHeight) { + _fullscreenMenuWidget->setFixedHeight(0); + Menu::getInstance()->setFixedHeight(0); + } else { + _fullscreenMenuWidget->setFixedHeight(_menuBarHeight); + Menu::getInstance()->setFixedHeight(_menuBarHeight); + } + } + _entities.mouseMoveEvent(event, deviceID); _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts @@ -1449,6 +1464,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) { if (_controllerScriptingInterface.isMouseCaptured()) { return; } + } void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { @@ -1735,6 +1751,34 @@ void Application::setFullscreen(bool fullscreen) { Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(fullscreen); } +// The following code block is useful on platforms that can have a visible +// app menu in a fullscreen window. However the OSX mechanism hides the +// application menu for fullscreen apps, so the check is not required. +#ifndef Q_OS_MAC + if (Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode)) { + if (fullscreen) { + // Menu hide() disables menu commands, and show() after hide() doesn't work with Rift VR display. + // So set height instead. + _window->menuBar()->setMaximumHeight(0); + } else { + _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); + } + } else { + if (fullscreen) { + // Move menu to a QWidget floating above _glWidget so that show/hide doesn't adjust viewport. + _menuBarHeight = Menu::getInstance()->height(); + Menu::getInstance()->setParent(_fullscreenMenuWidget); + Menu::getInstance()->setFixedWidth(_window->windowHandle()->screen()->size().width()); + _fullscreenMenuWidget->show(); + } else { + // Restore menu to being part of MainWindow. + _fullscreenMenuWidget->hide(); + _window->setMenuBar(Menu::getInstance()); + _window->menuBar()->setMaximumHeight(QWIDGETSIZE_MAX); + } + } +#endif + // Work around Qt bug that prevents floating menus being shown when in fullscreen mode. // https://bugreports.qt.io/browse/QTBUG-41883 // Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around. @@ -1987,18 +2031,16 @@ void Application::init() { OculusManager::connect(); if (OculusManager::isConnected()) { - // perform as a post-event so that the code is run after init is complete - qApp->postLambdaEvent([] { - Menu::getInstance()->triggerOption(MenuOption::Fullscreen); - }); + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); } TV3DManager::connect(); if (TV3DManager::isConnected()) { - // perform as a post-event so that the code is run after init is complete - qApp->postLambdaEvent([] { - Menu::getInstance()->triggerOption(MenuOption::Fullscreen); - }); + QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen), + "trigger", + Qt::QueuedConnection); } _timerStart.start(); diff --git a/interface/src/Application.h b/interface/src/Application.h index f63f548c47..688cf23876 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -623,6 +623,9 @@ private: void checkSkeleton(); + QWidget* _fullscreenMenuWidget = new QWidget(); + int _menuBarHeight; + QHash _acceptedExtensions; QList _domainConnectionRefusals; diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 76ab607ff5..896a50acca 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -84,16 +84,13 @@ void Bookmarks::persistToFile() { saveFile.write(data); } -void Bookmarks::setupMenus(const QString & parentMenu) { +void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { // Add menus/actions - Menu * menu = Menu::getInstance(); - menu->addItem(parentMenu, MenuOption::BookmarkLocation, [this] { - bookmarkLocation(); - }); - menu->addMenu(parentMenu, MenuOption::Bookmarks); - menu->addItem(parentMenu, MenuOption::DeleteBookmark, [this] { - deleteBookmark(); - }); + menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0, + this, SLOT(bookmarkLocation())); + _bookmarksMenu = menu->addMenu(MenuOption::Bookmarks); + _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark, 0, + this, SLOT(deleteBookmark())); // Enable/Disable menus as needed enableMenuItems(_bookmarks.count() > 0); @@ -102,7 +99,7 @@ void Bookmarks::setupMenus(const QString & parentMenu) { for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) { QString bookmarkName = it.key(); QString bookmarkAddress = it.value().toString(); - addLocationToMenu(bookmarkName, bookmarkAddress); + addLocationToMenu(menubar, bookmarkName, bookmarkAddress); } } @@ -137,20 +134,29 @@ void Bookmarks::bookmarkLocation() { if (duplicateBookmarkMessage.exec() == QMessageBox::No) { return; } - removeLocationFromMenu(bookmarkName); + removeLocationFromMenu(menubar, bookmarkName); } - addLocationToMenu(bookmarkName, bookmarkAddress); + addLocationToMenu(menubar, bookmarkName, bookmarkAddress); insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. enableMenuItems(true); } +void Bookmarks::teleportToBookmark() { + QAction* action = qobject_cast(sender()); + QString address = action->data().toString(); + DependencyManager::get()->handleLookupString(address); +} + void Bookmarks::deleteBookmark() { + QStringList bookmarkList; - bookmarkList.append(_bookmarks.keys()); - - + QList menuItems = _bookmarksMenu->actions(); + for (int i = 0; i < menuItems.count(); i += 1) { + bookmarkList.append(menuItems[i]->text()); + } + QInputDialog deleteBookmarkDialog(qApp->getWindow()); deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); @@ -168,29 +174,34 @@ void Bookmarks::deleteBookmark() { return; } - removeLocationFromMenu(bookmarkName); + removeLocationFromMenu(Menu::getInstance(), bookmarkName); remove(bookmarkName); - - if (_bookmarks.keys().isEmpty()) { + + if (_bookmarksMenu->actions().count() == 0) { enableMenuItems(false); } } void Bookmarks::enableMenuItems(bool enabled) { - Menu* menu = Menu::getInstance(); - menu->enableItem(MenuOption::Bookmarks, enabled); - menu->enableItem(MenuOption::DeleteBookmark, enabled); + if (_bookmarksMenu) { + _bookmarksMenu->setEnabled(enabled); + } + if (_deleteBookmarksAction) { + _deleteBookmarksAction->setEnabled(enabled); + } } -void Bookmarks::addLocationToMenu(QString& name, QString& address) { - - Menu::getInstance()->addItem(MenuOption::Bookmarks, name, [=] { - DependencyManager::get()->handleLookupString(address); - }); +void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { + QAction* teleportAction = _bookmarksMenu->newAction(); + teleportAction->setData(address); + connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); + + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, + name, 0, QAction::NoRole); } -void Bookmarks::removeLocationFromMenu(QString& name) { - Menu::getInstance()->removeItem(name); +void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) { + menubar->removeAction(_bookmarksMenu, name); } diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index d996d7c0f0..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,16 +27,19 @@ class Bookmarks: public QObject { public: Bookmarks(); - void setupMenus(const QString & menu); + void setupMenus(Menu* menubar, MenuWrapper* menu); private slots: void bookmarkLocation(); + void teleportToBookmark(); void deleteBookmark(); private: - // FIXME bookmarks should be more categorizable - // Can we leverage a system browser favorites API? QVariantMap _bookmarks; // { name: address, ... } + + QPointer _bookmarksMenu; + QPointer _deleteBookmarksAction; + const QString BOOKMARKS_FILENAME = "bookmarks.json"; QString _bookmarksFilename; @@ -47,8 +51,8 @@ private: void persistToFile(); void enableMenuItems(bool enabled); - void addLocationToMenu(QString& name, QString& address); - void removeLocationFromMenu(QString& name); + void addLocationToMenu(Menu* menubar, QString& name, QString& address); + void removeLocationFromMenu(Menu* menubar, QString& name); }; #endif // hifi_Bookmarks_h \ No newline at end of file diff --git a/interface/src/MainWindow.cpp b/interface/src/MainWindow.cpp index 7e8d46f5ee..f60716dc48 100644 --- a/interface/src/MainWindow.cpp +++ b/interface/src/MainWindow.cpp @@ -94,9 +94,9 @@ void MainWindow::changeEvent(QEvent* event) { emit windowShown(true); } - //if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { - // Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); - //} + if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { + Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); + } } else if (event->type() == QEvent::ActivationChange) { if (isActiveWindow()) { emit windowShown(true); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 8c45f583f1..67fae46b33 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include @@ -21,7 +20,8 @@ #include #include #include -#include +#include + #include "Application.h" #include "AccountManager.h" #include "audio/AudioIOStatsRenderer.h" @@ -42,625 +42,529 @@ #include "InterfaceLogging.h" #include "Menu.h" -#include "Util.h" -// Proxy object to simplify porting over -HifiAction::HifiAction(const QString& menuOption) : _menuOption(menuOption) { -} - -void HifiAction::setCheckable(bool checkable) { - Menu::getInstance()->setCheckable(_menuOption, checkable); -} - -void HifiAction::setChecked(bool checked) { - Menu::getInstance()->setChecked(_menuOption, checked); -} - -void HifiAction::setVisible(bool visible) { - return Menu::getInstance()->setItemVisible(_menuOption); -} - -QString HifiAction::shortcut() const { - return Menu::getInstance()->getItemShortcut(_menuOption); -} - -void HifiAction::setText(const QString& text) { - Menu::getInstance()->setItemText(_menuOption, text); -} - -void HifiAction::setTriggerAction(std::function f) { - Menu::getInstance()->setTriggerAction(_menuOption, f); -} - -void HifiAction::setToggleAction(std::function f) { - Menu::getInstance()->setToggleAction(_menuOption, f); -} - -Menu* Menu::_instance = nullptr; +Menu* Menu::_instance = NULL; Menu* Menu::getInstance() { - // Optimistic check for menu existence + static QMutex menuInstanceMutex; + + // lock the menu instance mutex to make sure we don't race and create two menus and crash + menuInstanceMutex.lock(); + if (!_instance) { - static QMutex menuInstanceMutex; - withLock(menuInstanceMutex, [] { - if (!_instance) { - qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); - qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); - Menu::load(); - auto uiRoot = DependencyManager::get()->getRootItem(); - _instance = uiRoot->findChild(NAME); - if (!_instance) { - qFatal("Could not load menu QML"); - } else { - _instance->init(); - } - } - }); + qCDebug(interfaceapp, "First call to Menu::getInstance() - initing menu."); + _instance = new Menu(); } + + menuInstanceMutex.unlock(); + return _instance; } -Menu::Menu(QQuickItem* parent) : HifiMenu(parent) { -} - -void Menu::init() { +Menu::Menu() { + MenuWrapper * fileMenu = addMenu("File"); +#ifdef Q_OS_MAC + addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); +#endif auto dialogsManager = DependencyManager::get(); AccountManager& accountManager = AccountManager::getInstance(); - static const QString ROOT_MENU; + { - static const QString FILE_MENU{ "File" }; - addMenu(ROOT_MENU, FILE_MENU); - { - addItem(FILE_MENU, MenuOption::Login); - // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item - connect(&accountManager, &AccountManager::profileChanged, + addActionToQMenuAndActionHash(fileMenu, MenuOption::Login); + + // connect to the appropriate signal of the AccountManager so that we can change the Login/Logout menu item + connect(&accountManager, &AccountManager::profileChanged, dialogsManager.data(), &DialogsManager::toggleLoginDialog); - connect(&accountManager, &AccountManager::logoutComplete, + connect(&accountManager, &AccountManager::logoutComplete, dialogsManager.data(), &DialogsManager::toggleLoginDialog); - } - -#ifdef Q_OS_MAC - addActionToQMenuAndActionHash(fileMenu, MenuOption::AboutApp, 0, qApp, SLOT(aboutApp()), QAction::AboutRole); -#endif - - { -#if 0 - static const QString SCRIPTS_MENU{ "Scripts" }; - addMenu(FILE_MENU, LOCATION_MENU); -#else - static const QString SCRIPTS_MENU{ FILE_MENU }; - addSeparator(FILE_MENU, "Scripts"); -#endif - //Qt::CTRL | Qt::Key_O - addItem(SCRIPTS_MENU, MenuOption::LoadScript, qApp, SLOT(loadDialog())); - //Qt::CTRL | Qt::SHIFT | Qt::Key_O - addItem(SCRIPTS_MENU, MenuOption::LoadScriptURL, [=] { - qApp->loadScriptURLDialog(); - }); - addItem(SCRIPTS_MENU, MenuOption::StopAllScripts, [=] { - qApp->stopAllScripts(); - }); - //Qt::CTRL | Qt::Key_R, - addItem(SCRIPTS_MENU, MenuOption::ReloadAllScripts, [=] { - qApp->reloadAllScripts(); - }); - // Qt::CTRL | Qt::Key_J, - addItem(SCRIPTS_MENU, MenuOption::RunningScripts, [=] { - qApp->toggleRunningScriptsWidget(); - }); - } - - { -#if 0 - static const QString LOCATION_MENU{ "Location" }; - addMenu(FILE_MENU, LOCATION_MENU); -#else - addSeparator(FILE_MENU, "Location"); - static const QString LOCATION_MENU{ FILE_MENU }; -#endif - qApp->getBookmarks()->setupMenus(LOCATION_MENU); - - //Qt::CTRL | Qt::Key_L - addItem(LOCATION_MENU, MenuOption::AddressBar, [=] { - auto dialogsManager = DependencyManager::get(); - dialogsManager->toggleAddressBar(); - }); - addItem(LOCATION_MENU, MenuOption::CopyAddress, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyAddress(); - }); - addItem(LOCATION_MENU, MenuOption::CopyPath, [=] { - auto addressManager = DependencyManager::get(); - addressManager->copyPath(); - }); - } - - // Qt::CTRL | Qt::Key_Q - // QAction::QuitRole - addItem(FILE_MENU, MenuOption::Quit, [=] { - qApp->quit(); - }); } + + addDisabledActionAndSeparator(fileMenu, "Scripts"); + addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScript, Qt::CTRL | Qt::Key_O, + qApp, SLOT(loadDialog())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::LoadScriptURL, + Qt::CTRL | Qt::SHIFT | Qt::Key_O, qApp, SLOT(loadScriptURLDialog())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::StopAllScripts, 0, qApp, SLOT(stopAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::ReloadAllScripts, Qt::CTRL | Qt::Key_R, + qApp, SLOT(reloadAllScripts())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::RunningScripts, Qt::CTRL | Qt::Key_J, + qApp, SLOT(toggleRunningScriptsWidget())); + + addDisabledActionAndSeparator(fileMenu, "Location"); + qApp->getBookmarks()->setupMenus(this, fileMenu); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::AddressBar, + Qt::CTRL | Qt::Key_L, + dialogsManager.data(), + SLOT(toggleAddressBar())); + auto addressManager = DependencyManager::get(); + addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyAddress, 0, + addressManager.data(), SLOT(copyAddress())); + addActionToQMenuAndActionHash(fileMenu, MenuOption::CopyPath, 0, + addressManager.data(), SLOT(copyPath())); + + addActionToQMenuAndActionHash(fileMenu, + MenuOption::Quit, + Qt::CTRL | Qt::Key_Q, + qApp, + SLOT(quit()), + QAction::QuitRole); - { - static const QString EDIT_MENU{ "Edit" }; - addMenu(ROOT_MENU, EDIT_MENU); + MenuWrapper* editMenu = addMenu("Edit"); - QUndoStack* undoStack = qApp->getUndoStack(); - QAction* undoAction = undoStack->createUndoAction(this); - //undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); - addItem(EDIT_MENU, undoAction->text(), undoAction, SLOT(trigger())); + QUndoStack* undoStack = qApp->getUndoStack(); + QAction* undoAction = undoStack->createUndoAction(editMenu); + undoAction->setShortcut(Qt::CTRL | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, undoAction); - QAction* redoAction = undoStack->createRedoAction(this); - //redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); - addItem(EDIT_MENU, redoAction->text(), redoAction, SLOT(trigger())); + QAction* redoAction = undoStack->createRedoAction(editMenu); + redoAction->setShortcut(Qt::CTRL | Qt::SHIFT | Qt::Key_Z); + addActionToQMenuAndActionHash(editMenu, redoAction); - // Qt::CTRL | Qt::Key_Comma - // QAction::PreferencesRole - addItem(EDIT_MENU, MenuOption::Preferences, [=] { - dialogsManager->editPreferences(); - }); - addItem(EDIT_MENU, MenuOption::Animations, [=] { - dialogsManager->editAnimations(); - }); - } + addActionToQMenuAndActionHash(editMenu, + MenuOption::Preferences, + Qt::CTRL | Qt::Key_Comma, + dialogsManager.data(), + SLOT(editPreferences()), + QAction::PreferencesRole); - { - static const QString TOOLS_MENU{ "Tools" }; - addMenu(ROOT_MENU, TOOLS_MENU); + addActionToQMenuAndActionHash(editMenu, MenuOption::Attachments, 0, + dialogsManager.data(), SLOT(editAttachments())); + addActionToQMenuAndActionHash(editMenu, MenuOption::Animations, 0, + dialogsManager.data(), SLOT(editAnimations())); + + MenuWrapper* toolsMenu = addMenu("Tools"); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::ScriptEditor, Qt::ALT | Qt::Key_S, + dialogsManager.data(), SLOT(showScriptEditor())); #if defined(Q_OS_MAC) || defined(Q_OS_WIN) - auto speechRecognizer = DependencyManager::get(); - //Qt::CTRL | Qt::SHIFT | Qt::Key_C - addItem(TOOLS_MENU, MenuOption::ControlWithSpeech); - setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); - connect(speechRecognizer.data(), &SpeechRecognizer::enabledUpdated, [=] { - setChecked(MenuOption::ControlWithSpeech, speechRecognizer->getEnabled()); - }); + auto speechRecognizer = DependencyManager::get(); + QAction* speechRecognizerAction = addCheckableActionToQMenuAndActionHash(toolsMenu, MenuOption::ControlWithSpeech, + Qt::CTRL | Qt::SHIFT | Qt::Key_C, + speechRecognizer->getEnabled(), + speechRecognizer.data(), + SLOT(setEnabled(bool))); + connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool))); #endif - // Qt::ALT | Qt::Key_S, - addItem(TOOLS_MENU, MenuOption::ScriptEditor, [=] { - dialogsManager->showScriptEditor(); - }); - // QML Qt::Key_Backslash, - addItem(TOOLS_MENU, MenuOption::Chat, [=] { - dialogsManager->showIRCLink(); - }); - addItem(TOOLS_MENU, MenuOption::AddRemoveFriends, [=] { - qApp->showFriendsWindow(); - }); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, + 0, // QML Qt::Key_Backslash, + dialogsManager.data(), SLOT(showIRCLink())); + addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0, + qApp, SLOT(showFriendsWindow())); - { - static const QString VIZ_MENU{ "I Am Visible To" }; - addMenu(TOOLS_MENU, VIZ_MENU); - auto discoverabilityManager = DependencyManager::get(); - // FIXME group - addCheckableItem(VIZ_MENU, MenuOption::VisibleToEveryone, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, - discoverabilityManager.data(), SLOT(setVisibility())); - addCheckableItem(VIZ_MENU, MenuOption::VisibleToFriends, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, - discoverabilityManager.data(), SLOT(setVisibility())); - addCheckableItem(VIZ_MENU, MenuOption::VisibleToNoOne, - discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, - discoverabilityManager.data(), SLOT(setVisibility())); - connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, - discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); - } + MenuWrapper* visibilityMenu = toolsMenu->addMenu("I Am Visible To"); + { + QActionGroup* visibilityGroup = new QActionGroup(toolsMenu); + auto discoverabilityManager = DependencyManager::get(); - //Qt::CTRL | Qt::ALT | Qt::Key_T, - addItem(TOOLS_MENU, MenuOption::ToolWindow, - dialogsManager.data(), SLOT(toggleToolWindow())); + QAction* visibleToEveryone = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToEveryone, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::All, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToEveryone); - //Qt::CTRL | Qt::ALT | Qt::Key_J, - addItem(TOOLS_MENU, MenuOption::Console, [=] { - DependencyManager::get()->toggleConsole(); - }); + QAction* visibleToFriends = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToFriends, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::Friends, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToFriends); - // QML Qt::Key_Apostrophe, - addItem(TOOLS_MENU, MenuOption::ResetSensors, [=] { - qApp->resetSensors(); - }); + QAction* visibleToNoOne = addCheckableActionToQMenuAndActionHash(visibilityMenu, MenuOption::VisibleToNoOne, + 0, discoverabilityManager->getDiscoverabilityMode() == Discoverability::None, + discoverabilityManager.data(), SLOT(setVisibility())); + visibilityGroup->addAction(visibleToNoOne); - addItem(TOOLS_MENU, MenuOption::PackageModel, [=] { - qApp->packageModel(); - }); + connect(discoverabilityManager.data(), &DiscoverabilityManager::discoverabilityModeChanged, + discoverabilityManager.data(), &DiscoverabilityManager::visibilityChanged); } - { - static const QString AVATAR_MENU{ "Avatar" }; - addMenu(ROOT_MENU, AVATAR_MENU); - auto avatar = DependencyManager::get()->getMyAvatar(); - { - static const QString SIZE_MENU{ "Size" }; - addMenu(AVATAR_MENU, SIZE_MENU); - // QML Qt::Key_Plus, - addItem(SIZE_MENU, MenuOption::IncreaseAvatarSize, [=] { - avatar->increaseSize(); - }); - // QML Qt::Key_Minus, - addItem(SIZE_MENU, MenuOption::DecreaseAvatarSize, [=] { - avatar->decreaseSize(); - }); - // QML Qt::Key_Equal, - addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { - avatar->resetSize(); - }); + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ToolWindow, + Qt::CTRL | Qt::ALT | Qt::Key_T, + dialogsManager.data(), + SLOT(toggleToolWindow())); - addItem(SIZE_MENU, MenuOption::ResetAvatarSize, [=] { - avatar->resetSize(); - }); - } + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::Console, + Qt::CTRL | Qt::ALT | Qt::Key_J, + DependencyManager::get().data(), + SLOT(toggleConsole())); - //Qt::CTRL | Qt::SHIFT | Qt::Key_K - addCheckableItem(AVATAR_MENU, MenuOption::KeyboardMotorControl, true, [=](bool) { - avatar->updateMotionBehavior(); - }); - addCheckableItem(AVATAR_MENU, MenuOption::ScriptedMotorControl, true); - addCheckableItem(AVATAR_MENU, MenuOption::NamesAboveHeads, true); - addCheckableItem(AVATAR_MENU, MenuOption::GlowWhenSpeaking, true); - addCheckableItem(AVATAR_MENU, MenuOption::BlueSpeechSphere, true); - addCheckableItem(AVATAR_MENU, MenuOption::EnableCharacterController, true, + addActionToQMenuAndActionHash(toolsMenu, + MenuOption::ResetSensors, + 0, // QML Qt::Key_Apostrophe, + qApp, + SLOT(resetSensors())); + + addActionToQMenuAndActionHash(toolsMenu, MenuOption::PackageModel, 0, + qApp, SLOT(packageModel())); + + MenuWrapper* avatarMenu = addMenu("Avatar"); + QObject* avatar = DependencyManager::get()->getMyAvatar(); + + MenuWrapper* avatarSizeMenu = avatarMenu->addMenu("Size"); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::IncreaseAvatarSize, + 0, // QML Qt::Key_Plus, + avatar, + SLOT(increaseSize())); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::DecreaseAvatarSize, + 0, // QML Qt::Key_Minus, + avatar, + SLOT(decreaseSize())); + addActionToQMenuAndActionHash(avatarSizeMenu, + MenuOption::ResetAvatarSize, + 0, // QML Qt::Key_Equal, + avatar, + SLOT(resetSize())); + + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::KeyboardMotorControl, + Qt::CTRL | Qt::SHIFT | Qt::Key_K, true, avatar, SLOT(updateMotionBehavior())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ScriptedMotorControl, 0, true, avatar, SLOT(updateMotionBehavior())); - addCheckableItem(AVATAR_MENU, MenuOption::ShiftHipsForIdleAnimations, false, + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::NamesAboveHeads, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::GlowWhenSpeaking, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::BlueSpeechSphere, 0, true); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::EnableCharacterController, 0, true, + avatar, SLOT(updateMotionBehavior())); + addCheckableActionToQMenuAndActionHash(avatarMenu, MenuOption::ShiftHipsForIdleAnimations, 0, false, avatar, SLOT(updateMotionBehavior())); - } - { - static const QString VIEW_MENU{ "View" }; - addMenu(ROOT_MENU, VIEW_MENU); + MenuWrapper* viewMenu = addMenu("View"); - // Mac Qt::CTRL | Qt::META | Qt::Key_F, - // Win32/Linux Qt::CTRL | Qt::Key_F, - addCheckableItem(VIEW_MENU, MenuOption::Fullscreen, false); - connectCheckable(MenuOption::Fullscreen, qApp, SLOT(setFullscreen(bool))); - // QML Qt::Key_P, - addCheckableItem(VIEW_MENU, MenuOption::FirstPerson, true, - qApp, SLOT(cameraMenuChanged())); - //QML Qt::SHIFT | Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::Mirror, true, - qApp, SLOT(cameraMenuChanged())); - // QML Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::FullscreenMirror, false, - qApp, SLOT(cameraMenuChanged())); - - - // Mac Qt::META | Qt::Key_H, - // Win32/Linux Qt::CTRL | Qt::Key_H, - addCheckableItem(VIEW_MENU, MenuOption::HMDTools, false, [=](bool checked) { - dialogsManager->hmdTools(checked); - }); - addCheckableItem(VIEW_MENU, MenuOption::EnableVRMode, false); - connectCheckable(MenuOption::EnableVRMode, qApp, SLOT(setEnableVRMode(bool))); - addCheckableItem(VIEW_MENU, MenuOption::Enable3DTVMode, false); - connectCheckable(MenuOption::Enable3DTVMode, qApp, SLOT(setEnable3DTVMode(bool))); - - { - static const QString BORDER_MENU{ "Server Borders" }; - addMenu(VIEW_MENU, BORDER_MENU); - // Qt::CTRL | Qt::SHIFT | Qt::Key_1 - addCheckableItem(BORDER_MENU, MenuOption::ShowBordersEntityNodes, false, [=](bool checked) { - qApp->getNodeBoundsDisplay().setShowEntityNodes(checked); - }); - } - addCheckableItem(VIEW_MENU, MenuOption::OffAxisProjection, false); - addCheckableItem(VIEW_MENU, MenuOption::TurnWithHead, false); - // QML Qt::Key_Slash - addCheckableItem(VIEW_MENU, MenuOption::Stats, false); - - // Qt::CTRL | Qt::SHIFT | Qt::Key_L - addItem(VIEW_MENU, MenuOption::Log, [=] { - qApp->toggleLogDialog(); - }); - addItem(VIEW_MENU, MenuOption::BandwidthDetails, [=] { - dialogsManager->bandwidthDetails(); - }); - addItem(VIEW_MENU, MenuOption::OctreeStats, [=] { - dialogsManager->octreeStatsDetails(); - }); - } - - { - static const QString DEV_MENU{ "Developer" }; - addMenu(ROOT_MENU, DEV_MENU); - { - static const QString RENDER_MENU{ "Render" }; - addMenu(DEV_MENU, RENDER_MENU); - // QML Qt::SHIFT | Qt::Key_A, - addCheckableItem(RENDER_MENU, MenuOption::Atmosphere, true); - addCheckableItem(RENDER_MENU, MenuOption::AmbientOcclusion); - addCheckableItem(RENDER_MENU, MenuOption::DontFadeOnOctreeServerChanges); - { - static const QString LIGHT_MENU{ MenuOption::RenderAmbientLight }; - addMenu(RENDER_MENU, LIGHT_MENU); - static QStringList LIGHTS{ - MenuOption::RenderAmbientLightGlobal, - MenuOption::RenderAmbientLight0, - MenuOption::RenderAmbientLight1, - MenuOption::RenderAmbientLight2, - MenuOption::RenderAmbientLight3, - MenuOption::RenderAmbientLight4, - MenuOption::RenderAmbientLight5, - MenuOption::RenderAmbientLight6, - MenuOption::RenderAmbientLight7, - MenuOption::RenderAmbientLight8, - MenuOption::RenderAmbientLight9, - }; - foreach(QString option, LIGHTS) { - addCheckableItem(LIGHT_MENU, option); - // FIXME - // setExclusiveGroup() - } - setChecked(MenuOption::RenderAmbientLightGlobal, true); - } - { - static const QString SHADOWS_MENU{ "Shadows" }; - addMenu(RENDER_MENU, SHADOWS_MENU); - addCheckableItem(SHADOWS_MENU, "No Shadows", true); - addCheckableItem(SHADOWS_MENU, MenuOption::SimpleShadows); - addCheckableItem(SHADOWS_MENU, MenuOption::CascadedShadows); - } - { - static const QString FRAMERATE_MENU{ MenuOption::RenderTargetFramerate }; - addMenu(RENDER_MENU, FRAMERATE_MENU); - //framerateGroup->setExclusive(true); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerateUnlimited, true); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate60); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate50); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate40); - addCheckableItem(FRAMERATE_MENU, MenuOption::RenderTargetFramerate30); - } -#if !defined(Q_OS_MAC) - addCheckableItem(RENDER_MENU, MenuOption::RenderTargetFramerateVSyncOn, true, [](bool checked) { - qApp->setVSyncEnabled(); - }); + addCheckableActionToQMenuAndActionHash(viewMenu, + MenuOption::Fullscreen, +#ifdef Q_OS_MAC + Qt::CTRL | Qt::META | Qt::Key_F, +#else + Qt::CTRL | Qt::Key_F, #endif - { - static const QString RES_MENU{ MenuOption::RenderResolution }; - addMenu(RENDER_MENU, RES_MENU); - // resolutionGroup->setExclusive(true); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionOne, true); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionTwoThird); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionHalf); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionThird); - addCheckableItem(RES_MENU, MenuOption::RenderResolutionQuarter); - } - // QML Qt::Key_Asterisk, - addCheckableItem(RENDER_MENU, MenuOption::Stars, true); - addCheckableItem(RENDER_MENU, MenuOption::EnableGlowEffect, true, [](bool checked){ - DependencyManager::get()->toggleGlowEffect(checked); - }); - //Qt::ALT | Qt::Key_W - addCheckableItem(RENDER_MENU, MenuOption::Wireframe); - // QML Qt::SHIFT | Qt::Key_L, - addItem(RENDER_MENU, MenuOption::LodTools, [=] { - dialogsManager->lodTools(); - }); - } // Developer -> Render + false, + qApp, + SLOT(setFullscreen(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, + 0, // QML Qt::Key_P, + true, qApp, SLOT(cameraMenuChanged())); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, + 0, //QML Qt::SHIFT | Qt::Key_H, + true); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, + 0, // QML Qt::Key_H, + false, qApp, SLOT(cameraMenuChanged())); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools, +#ifdef Q_OS_MAC + Qt::META | Qt::Key_H, +#else + Qt::CTRL | Qt::Key_H, +#endif + false, + dialogsManager.data(), + SLOT(hmdTools(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::EnableVRMode, 0, + false, + qApp, + SLOT(setEnableVRMode(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Enable3DTVMode, 0, + false, + qApp, + SLOT(setEnable3DTVMode(bool))); + + + MenuWrapper* nodeBordersMenu = viewMenu->addMenu("Server Borders"); + NodeBounds& nodeBounds = qApp->getNodeBoundsDisplay(); + addCheckableActionToQMenuAndActionHash(nodeBordersMenu, MenuOption::ShowBordersEntityNodes, + Qt::CTRL | Qt::SHIFT | Qt::Key_1, false, + &nodeBounds, SLOT(setShowEntityNodes(bool))); + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::OffAxisProjection, 0, false); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false); + + + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, + 0); // QML Qt::Key_Slash); + addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats); + addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, + Qt::CTRL | Qt::SHIFT | Qt::Key_L, + qApp, SLOT(toggleLogDialog())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0, + dialogsManager.data(), SLOT(bandwidthDetails())); + addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0, + dialogsManager.data(), SLOT(octreeStatsDetails())); + + + MenuWrapper* developerMenu = addMenu("Developer"); + + 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); + + MenuWrapper* ambientLightMenu = renderOptionsMenu->addMenu(MenuOption::RenderAmbientLight); + QActionGroup* ambientLightGroup = new QActionGroup(ambientLightMenu); + ambientLightGroup->setExclusive(true); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLightGlobal, 0, true)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight0, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight1, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight2, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight3, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight4, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight5, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight6, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight7, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight8, 0, false)); + ambientLightGroup->addAction(addCheckableActionToQMenuAndActionHash(ambientLightMenu, MenuOption::RenderAmbientLight9, 0, false)); + + 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)); + + { + MenuWrapper* framerateMenu = renderOptionsMenu->addMenu(MenuOption::RenderTargetFramerate); + QActionGroup* framerateGroup = new QActionGroup(framerateMenu); + framerateGroup->setExclusive(true); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerateUnlimited, 0, true)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate60, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate50, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate40, 0, false)); + framerateGroup->addAction(addCheckableActionToQMenuAndActionHash(framerateMenu, MenuOption::RenderTargetFramerate30, 0, false)); + +#if defined(Q_OS_MAC) +#else + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::RenderTargetFramerateVSyncOn, 0, true, + qApp, SLOT(setVSyncEnabled())); +#endif + } + + + MenuWrapper* resolutionMenu = renderOptionsMenu->addMenu(MenuOption::RenderResolution); + QActionGroup* resolutionGroup = new QActionGroup(resolutionMenu); + resolutionGroup->setExclusive(true); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionOne, 0, true)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionTwoThird, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionHalf, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false)); + resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false)); + + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, + 0, // QML Qt::Key_Asterisk, + true); + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true, + DependencyManager::get().data(), SLOT(toggleGlowEffect(bool))); + + addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false); + addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, + 0, // QML Qt::SHIFT | Qt::Key_L, + dialogsManager.data(), SLOT(lodTools())); + + MenuWrapper* avatarDebugMenu = developerMenu->addMenu("Avatar"); + + MenuWrapper* faceTrackingMenu = avatarDebugMenu->addMenu("Face Tracking"); + { + QActionGroup* faceTrackerGroup = new QActionGroup(avatarDebugMenu); + + QAction* noFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::NoFaceTracking, + 0, true, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(noFaceTracker); - { - static const QString AVATAR_MENU{ "Avatar Dev" }; - addMenu(DEV_MENU, AVATAR_MENU); - setItemText(AVATAR_MENU, "Avatar"); - { - static const QString FACE_MENU{ "Face Tracking" }; - addMenu(AVATAR_MENU, FACE_MENU); - // FIXME GROUP - addCheckableItem(FACE_MENU, MenuOption::NoFaceTracking, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); #ifdef HAVE_FACESHIFT - addCheckableItem(FACE_MENU, MenuOption::Faceshift, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); + QAction* faceshiftFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::Faceshift, + 0, false, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(faceshiftFaceTracker); #endif #ifdef HAVE_DDE - addCheckableItem(FACE_MENU, MenuOption::UseCamera, true, [](bool checked) { - qApp->setActiveFaceTracker(); - }); + QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera, + 0, false, + qApp, SLOT(setActiveFaceTracker())); + faceTrackerGroup->addAction(ddeFaceTracker); +#endif + } +#ifdef HAVE_DDE + faceTrackingMenu->addSeparator(); + QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true); + useAudioForMouth->setVisible(false); + QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true); + ddeFiltering->setVisible(false); #endif - addCheckableItem(FACE_MENU, MenuOption::UseAudioForMouth, true); - // FIXME integrate the visibility into the main API - getActionForOption(MenuOption::UseAudioForMouth)->setVisible(false); - addCheckableItem(FACE_MENU, MenuOption::VelocityFilter, true); - // FIXME integrate the visibility into the main API - getActionForOption(MenuOption::VelocityFilter)->setVisible(false); - } - addCheckableItem(AVATAR_MENU, MenuOption::RenderSkeletonCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderHeadCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderBoundingCollisionShapes); - addCheckableItem(AVATAR_MENU, MenuOption::RenderLookAtVectors); - addCheckableItem(AVATAR_MENU, MenuOption::RenderFocusIndicator); - { - static const QString HANDS_MENU{ "Hands" }; - addMenu(AVATAR_MENU, HANDS_MENU); - addCheckableItem(HANDS_MENU, MenuOption::AlignForearmsWithWrists); - addCheckableItem(HANDS_MENU, MenuOption::AlternateIK); - addCheckableItem(HANDS_MENU, MenuOption::DisplayHands, true); - addCheckableItem(HANDS_MENU, MenuOption::DisplayHandTargets); - addCheckableItem(HANDS_MENU, MenuOption::ShowIKConstraints); - { - static const QString SIXENSE_MENU{ "Sixense" }; - addMenu(HANDS_MENU, SIXENSE_MENU); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderHeadCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderBoundingCollisionShapes); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderLookAtVectors, 0, false); + addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false); + + 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); + + MenuWrapper* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense"); #ifdef __APPLE__ - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseEnabled, false, [](bool checked) { - SixenseManager::getInstance().toggleSixense(checked); - }); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::SixenseEnabled, + 0, false, + &SixenseManager::getInstance(), + SLOT(toggleSixense(bool))); #endif - addCheckableItem(SIXENSE_MENU, MenuOption::FilterSixense, true, [](bool checked) { - SixenseManager::getInstance().setFilter(checked); - }); - addCheckableItem(SIXENSE_MENU, MenuOption::LowVelocityFilter, true, [](bool checked) { - qApp->setLowVelocityFilter(checked); - }); - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseMouseInput, true); - addCheckableItem(SIXENSE_MENU, MenuOption::SixenseLasers); - } + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::FilterSixense, + 0, + true, + &SixenseManager::getInstance(), + SLOT(setFilter(bool))); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, + MenuOption::LowVelocityFilter, + 0, + true, + qApp, + SLOT(setLowVelocityFilter(bool))); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseMouseInput, 0, true); + addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu, MenuOption::SixenseLasers, 0, false); - { - static const QString LEAP_MENU{ "Leap Motion" }; - addMenu(HANDS_MENU, LEAP_MENU); - addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); - addCheckableItem(LEAP_MENU, MenuOption::LeapMotionOnHMD); - } + MenuWrapper* leapOptionsMenu = handOptionsMenu->addMenu("Leap Motion"); + addCheckableActionToQMenuAndActionHash(leapOptionsMenu, MenuOption::LeapMotionOnHMD, 0, false); #ifdef HAVE_RSSDK - { - static const QString RSS_MENU{ "RealSense" }; - addMenu(HANDS_MENU, RSS_MENU); - addItem(RSS_MENU, MenuOption::LoadRSSDKFile, [] { - RealSense::getInstance()->loadRSSDKFile(); - }); - addCheckableItem(RSS_MENU, MenuOption::LeapMotionOnHMD); - } + QMenu* realSenseOptionsMenu = handOptionsMenu->addMenu("RealSense"); + addActionToQMenuAndActionHash(realSenseOptionsMenu, MenuOption::LoadRSSDKFile, 0, + RealSense::getInstance(), SLOT(loadRSSDKFile())); #endif - } // Developer -> Hands - { - static const QString NETWORK_MENU{ "Network" }; - addMenu(DEV_MENU, NETWORK_MENU); - addCheckableItem(NETWORK_MENU, MenuOption::DisableNackPackets); - addCheckableItem(NETWORK_MENU, MenuOption::DisableActivityLogger, false, [](bool checked) { - UserActivityLogger::getInstance().disable(checked); - }); - addItem(NETWORK_MENU, MenuOption::CachesSize, [=] { - dialogsManager->cachesSizeDialog(); - }); - addItem(NETWORK_MENU, MenuOption::DiskCacheEditor, [=] { - dialogsManager->toggleDiskCacheEditor(); - }); - } // Developer -> Network + MenuWrapper* networkMenu = developerMenu->addMenu("Network"); + addCheckableActionToQMenuAndActionHash(networkMenu, MenuOption::DisableNackPackets, 0, false); + addCheckableActionToQMenuAndActionHash(networkMenu, + MenuOption::DisableActivityLogger, + 0, + false, + &UserActivityLogger::getInstance(), + SLOT(disable(bool))); + addActionToQMenuAndActionHash(networkMenu, MenuOption::CachesSize, 0, + dialogsManager.data(), SLOT(cachesSizeDialog())); + addActionToQMenuAndActionHash(networkMenu, MenuOption::DiskCacheEditor, 0, + dialogsManager.data(), SLOT(toggleDiskCacheEditor())); - { - static const QString TIMING_MENU{ "Timing and Stats" }; - addMenu(DEV_MENU, TIMING_MENU); - { - static const QString PERF_MENU{ "Performance Timer" }; - addMenu(TIMING_MENU, PERF_MENU); - addCheckableItem(PERF_MENU, MenuOption::DisplayDebugTimingDetails); - addCheckableItem(PERF_MENU, MenuOption::OnlyDisplayTopTen, true); - addCheckableItem(PERF_MENU, MenuOption::ExpandUpdateTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandMyAvatarSimulateTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandOtherAvatarTiming); - addCheckableItem(PERF_MENU, MenuOption::ExpandPaintGLTiming); - } - addCheckableItem(TIMING_MENU, MenuOption::TestPing, true); - addCheckableItem(TIMING_MENU, MenuOption::FrameTimer); - addItem(TIMING_MENU, MenuOption::RunTimingTests, [] { runTimingTests(); }); - addCheckableItem(TIMING_MENU, MenuOption::PipelineWarnings); - addCheckableItem(TIMING_MENU, MenuOption::SuppressShortTimings); - } // Developer -> Timing and Stats + 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); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandMyAvatarSimulateTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandOtherAvatarTiming, 0, false); + addCheckableActionToQMenuAndActionHash(perfTimerMenu, MenuOption::ExpandPaintGLTiming, 0, false); - { - static const QString AUDIO_MENU{ "Audio" }; - addMenu(DEV_MENU, AUDIO_MENU); - auto audioIO = DependencyManager::get(); - addCheckableItem(AUDIO_MENU, MenuOption::AudioNoiseReduction, true, - audioIO.data(), SLOT(toggleAudioNoiseReduction())); - addCheckableItem(AUDIO_MENU, MenuOption::EchoServerAudio, false, - audioIO.data(), SLOT(toggleServerEcho())); - addCheckableItem(AUDIO_MENU, MenuOption::EchoLocalAudio, false, - audioIO.data(), SLOT(toggleLocalEcho())); - addCheckableItem(AUDIO_MENU, MenuOption::StereoAudio, false, - audioIO.data(), SLOT(toggleStereoInput())); - // Qt::CTRL | Qt::Key_M, - addCheckableItem(AUDIO_MENU, MenuOption::MuteAudio, false, - audioIO.data(), SLOT(toggleMute())); - addCheckableItem(AUDIO_MENU, MenuOption::MuteEnvironment, false, - audioIO.data(), SLOT(sendMuteEnvironmentPacket())); - { - static const QString SCOPE_MENU{ "Audio Scope" }; - addMenu(AUDIO_MENU, SCOPE_MENU); - auto scope = DependencyManager::get(); - // Qt::CTRL | Qt::Key_P - addCheckableItem(SCOPE_MENU, MenuOption::AudioScope, false, [=](bool checked) { - scope->toggle(); - }); - // Qt::CTRL | Qt::SHIFT | Qt::Key_P - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopePause, false, [=](bool checked) { - scope->togglePause(); - }); - addSeparator(SCOPE_MENU, "Display Frames"); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::TestPing, 0, true); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::FrameTimer); + addActionToQMenuAndActionHash(timingMenu, MenuOption::RunTimingTests, 0, qApp, SLOT(runTests())); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::PipelineWarnings); + addCheckableActionToQMenuAndActionHash(timingMenu, MenuOption::SuppressShortTimings); - // FIXME GROUP - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiveFrames, true, [=](bool checked) { - scope->selectAudioScopeFiveFrames(); - }); - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeTwentyFrames, false, [=](bool checked) { - scope->selectAudioScopeTwentyFrames(); - }); - addCheckableItem(SCOPE_MENU, MenuOption::AudioScopeFiftyFrames, false, [=](bool checked) { - scope->selectAudioScopeFiftyFrames(); - }); - } - auto statsRenderer = DependencyManager::get(); + auto audioIO = DependencyManager::get(); + MenuWrapper* audioDebugMenu = developerMenu->addMenu("Audio"); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioNoiseReduction, + 0, + true, + audioIO.data(), + SLOT(toggleAudioNoiseReduction())); - // Qt::CTRL | Qt::SHIFT | Qt::Key_A, - addCheckableItem(AUDIO_MENU, MenuOption::AudioStats, false, [=](bool checked) { - statsRenderer->toggle(); - }); - addCheckableItem(AUDIO_MENU, MenuOption::AudioStatsShowInjectedStreams, false, [=](bool checked) { - statsRenderer->toggleShowInjectedStreams(); - }); - } // Developer -> Audio - } // Developer + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio, 0, false, + 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, + audioIO.data(), + SLOT(toggleMute())); + addActionToQMenuAndActionHash(audioDebugMenu, + MenuOption::MuteEnvironment, + 0, + audioIO.data(), + SLOT(sendMuteEnvironmentPacket())); + + auto scope = DependencyManager::get(); - { - static const QString HELP_MENU{ "Help" }; - addMenu(ROOT_MENU, HELP_MENU); - addItem(HELP_MENU, MenuOption::EditEntitiesHelp, [] { - qApp->showEditEntitiesHelp(); - }); - addItem(HELP_MENU, MenuOption::AboutApp, [] { - qApp->aboutApp(); - }); - } // Help + MenuWrapper* audioScopeMenu = audioDebugMenu->addMenu("Audio Scope"); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScope, + Qt::CTRL | Qt::Key_P, false, + scope.data(), + SLOT(toggle())); + addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopePause, + Qt::CTRL | Qt::SHIFT | Qt::Key_P , + false, + scope.data(), + SLOT(togglePause())); + addDisabledActionAndSeparator(audioScopeMenu, "Display Frames"); + { + QAction *fiveFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiveFrames, + 0, + true, + scope.data(), + SLOT(selectAudioScopeFiveFrames())); + + QAction *twentyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeTwentyFrames, + 0, + false, + scope.data(), + SLOT(selectAudioScopeTwentyFrames())); + + QAction *fiftyFrames = addCheckableActionToQMenuAndActionHash(audioScopeMenu, MenuOption::AudioScopeFiftyFrames, + 0, + false, + scope.data(), + SLOT(selectAudioScopeFiftyFrames())); + + QActionGroup* audioScopeFramesGroup = new QActionGroup(audioScopeMenu); + audioScopeFramesGroup->addAction(fiveFrames); + audioScopeFramesGroup->addAction(twentyFrames); + audioScopeFramesGroup->addAction(fiftyFrames); } + + auto statsRenderer = DependencyManager::get(); + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStats, + Qt::CTRL | Qt::SHIFT | Qt::Key_A, + false, + statsRenderer.data(), + SLOT(toggle())); + + addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::AudioStatsShowInjectedStreams, + 0, + false, + statsRenderer.data(), + SLOT(toggleShowInjectedStreams())); + + MenuWrapper* helpMenu = addMenu("Help"); + addActionToQMenuAndActionHash(helpMenu, MenuOption::EditEntitiesHelp, 0, qApp, SLOT(showEditEntitiesHelp())); + +#ifndef Q_OS_MAC + QAction* aboutAction = helpMenu->addAction(MenuOption::AboutApp); + connect(aboutAction, SIGNAL(triggered()), qApp, SLOT(aboutApp())); +#endif } void Menu::loadSettings() { -// scanMenuBar(&Menu::loadAction); + scanMenuBar(&Menu::loadAction); } void Menu::saveSettings() { -// scanMenuBar(&Menu::saveAction); + scanMenuBar(&Menu::saveAction); } -void Menu::addMenuItem(const MenuItemProperties& properties) { - if (QThread::currentThread() != Application::getInstance()->getMainThread()) { - Application::getInstance()->postLambdaEvent([=]{ - addMenuItem(properties); - }); - return; - } - // Shortcut key items: in order of priority - //QString shortcutKey; - //KeyEvent shortcutKeyEvent; - //QKeySequence shortcutKeySequence; // this is what we actually use, it's set from one of the above - - //// location related items: in order of priority - //int position; - //QString beforeItem; - //QString afterItem; - - if (properties.isSeparator) { - addSeparator(properties.menuName, properties.menuItemName); - return; - } - addItem(properties.menuName, properties.menuItemName); - if (properties.isCheckable) { - setCheckable(properties.menuItemName); - if (properties.isChecked) { - setChecked(properties.menuItemName); - } - } - } - -#if 0 - void Menu::loadAction(Settings& settings, QAction& action) { if (action.isChecked() != settings.value(action.text(), action.isChecked()).toBool()) { action.trigger(); @@ -690,7 +594,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]; @@ -710,5 +614,406 @@ void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& } } +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const QObject* receiver, + const char* member, + QAction::MenuRole role, + int menuItemLocation) { + QAction* action = NULL; + QAction* actionBefore = NULL; -#endif + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionBefore) { + if (receiver && member) { + action = destinationMenu->addAction(actionName, receiver, member, shortcut); + } else { + action = destinationMenu->addAction(actionName); + action->setShortcut(shortcut); + } + } else { + action = new QAction(actionName, destinationMenu); + action->setShortcut(shortcut); + destinationMenu->insertAction(actionBefore, action); + + if (receiver && member) { + connect(action, SIGNAL(triggered()), receiver, member); + } + } + action->setMenuRole(role); + + _actionHash.insert(actionName, action); + + return action; +} + +QAction* Menu::addActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName, + const QKeySequence& shortcut, + QAction::MenuRole role, + int menuItemLocation) { + QAction* actionBefore = NULL; + + if (menuItemLocation >= 0 && destinationMenu->actions().size() > menuItemLocation) { + actionBefore = destinationMenu->actions()[menuItemLocation]; + } + + if (!actionName.isEmpty()) { + action->setText(actionName); + } + + if (shortcut != 0) { + action->setShortcut(shortcut); + } + + if (role != QAction::NoRole) { + action->setMenuRole(role); + } + + if (!actionBefore) { + destinationMenu->addAction(action); + } else { + destinationMenu->insertAction(actionBefore, action); + } + + _actionHash.insert(action->text(), action); + + return action; +} + +QAction* Menu::addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut, + const bool checked, + const QObject* receiver, + const char* member, + int menuItemLocation) { + + QAction* action = addActionToQMenuAndActionHash(destinationMenu, actionName, shortcut, receiver, member, + QAction::NoRole, menuItemLocation); + action->setCheckable(true); + action->setChecked(checked); + + return action; +} + +void Menu::removeAction(MenuWrapper* menu, const QString& actionName) { + menu->removeAction(_actionHash.value(actionName)); + _actionHash.remove(actionName); +} + +void Menu::setIsOptionChecked(const QString& menuOption, bool isChecked) { + if (thread() != QThread::currentThread()) { + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); + return; + } + QAction* menu = _actionHash.value(menuOption); + if (menu) { + menu->setChecked(isChecked); + } +} + +bool Menu::isOptionChecked(const QString& menuOption) const { + const QAction* menu = _actionHash.value(menuOption); + if (menu) { + return menu->isChecked(); + } + return false; +} + +void Menu::triggerOption(const QString& menuOption) { + QAction* action = _actionHash.value(menuOption); + if (action) { + action->trigger(); + } else { + qCDebug(interfaceapp) << "NULL Action for menuOption '" << menuOption << "'"; + } +} + +QAction* Menu::getActionForOption(const QString& menuOption) { + return _actionHash.value(menuOption); +} + +QAction* Menu::getActionFromName(const QString& menuName, MenuWrapper* menu) { + QList menuActions; + if (menu) { + menuActions = menu->actions(); + } else { + menuActions = actions(); + } + + foreach (QAction* menuAction, menuActions) { + QString actionText = menuAction->text(); + if (menuName == menuAction->text()) { + return menuAction; + } + } + return NULL; +} + +MenuWrapper* Menu::getSubMenuFromName(const QString& menuName, MenuWrapper* menu) { + QAction* action = getActionFromName(menuName, menu); + if (action) { + return MenuWrapper::fromMenu(action->menu()); + } + return NULL; +} + +MenuWrapper* Menu::getMenuParent(const QString& menuName, QString& finalMenuPart) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + parent = menu; + finalMenuPart = menuTreePart.trimmed(); + menu = getSubMenuFromName(finalMenuPart, parent); + if (!menu) { + break; + } + } + return parent; +} + +MenuWrapper* Menu::getMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + MenuWrapper* menu = NULL; + int item = 0; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), parent); + if (!menu) { + break; + } + parent = menu; + item++; + } + return menu; +} + +QAction* Menu::getMenuAction(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* parent = NULL; + QAction* action = NULL; + foreach (QString menuTreePart, menuTree) { + action = getActionFromName(menuTreePart.trimmed(), parent); + if (!action) { + break; + } + parent = MenuWrapper::fromMenu(action->menu()); + } + return action; +} + +int Menu::findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem) { + int position = 0; + foreach(QAction* action, menu->actions()) { + if (action->text() == searchMenuItem) { + return position; + } + position++; + } + return UNSPECIFIED_POSITION; // not found +} + +int Menu::positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition) { + QList menuActions = menu->actions(); + if (requestedPosition > 1 && requestedPosition < menuActions.size()) { + QAction* beforeRequested = menuActions[requestedPosition - 1]; + if (beforeRequested->isSeparator()) { + requestedPosition--; + } + } + return requestedPosition; +} + + +MenuWrapper* Menu::addMenu(const QString& menuName) { + QStringList menuTree = menuName.split(">"); + MenuWrapper* addTo = NULL; + MenuWrapper* menu = NULL; + foreach (QString menuTreePart, menuTree) { + menu = getSubMenuFromName(menuTreePart.trimmed(), addTo); + if (!menu) { + if (!addTo) { + menu = new MenuWrapper(QMenuBar::addMenu(menuTreePart.trimmed())); + } else { + menu = addTo->addMenu(menuTreePart.trimmed()); + } + } + addTo = menu; + } + + QMenuBar::repaint(); + return menu; +} + +void Menu::removeMenu(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + QString finalMenuPart; + MenuWrapper* parent = getMenuParent(menuName, finalMenuPart); + if (parent) { + parent->removeAction(action); + } else { + QMenuBar::removeAction(action); + } + + QMenuBar::repaint(); + } +} + +bool Menu::menuExists(const QString& menuName) { + QAction* action = getMenuAction(menuName); + + // only proceed if the menu actually exists + if (action) { + return true; + } + return false; +} + +void Menu::addSeparator(const QString& menuName, const QString& separatorName) { + MenuWrapper* menuObj = getMenu(menuName); + if (menuObj) { + addDisabledActionAndSeparator(menuObj, separatorName); + } +} + +void Menu::removeSeparator(const QString& menuName, const QString& separatorName) { + MenuWrapper* menu = getMenu(menuName); + bool separatorRemoved = false; + if (menu) { + int textAt = findPositionOfMenuItem(menu, separatorName); + QList menuActions = menu->actions(); + QAction* separatorText = menuActions[textAt]; + if (textAt > 0 && textAt < menuActions.size()) { + QAction* separatorLine = menuActions[textAt - 1]; + if (separatorLine) { + if (separatorLine->isSeparator()) { + menu->removeAction(separatorText); + menu->removeAction(separatorLine); + separatorRemoved = true; + } + } + } + } + if (separatorRemoved) { + QMenuBar::repaint(); + } +} + +void Menu::addMenuItem(const MenuItemProperties& properties) { + MenuWrapper* menuObj = getMenu(properties.menuName); + if (menuObj) { + QShortcut* shortcut = NULL; + if (!properties.shortcutKeySequence.isEmpty()) { + shortcut = new QShortcut(properties.shortcutKeySequence, this); + } + + // check for positioning requests + int requestedPosition = properties.position; + if (requestedPosition == UNSPECIFIED_POSITION && !properties.beforeItem.isEmpty()) { + requestedPosition = findPositionOfMenuItem(menuObj, properties.beforeItem); + // double check that the requested location wasn't a separator label + requestedPosition = positionBeforeSeparatorIfNeeded(menuObj, requestedPosition); + } + if (requestedPosition == UNSPECIFIED_POSITION && !properties.afterItem.isEmpty()) { + int afterPosition = findPositionOfMenuItem(menuObj, properties.afterItem); + if (afterPosition != UNSPECIFIED_POSITION) { + requestedPosition = afterPosition + 1; + } + } + + QAction* menuItemAction = NULL; + if (properties.isSeparator) { + addDisabledActionAndSeparator(menuObj, properties.menuItemName, requestedPosition); + } else if (properties.isCheckable) { + menuItemAction = addCheckableActionToQMenuAndActionHash(menuObj, properties.menuItemName, + properties.shortcutKeySequence, properties.isChecked, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), requestedPosition); + } else { + menuItemAction = addActionToQMenuAndActionHash(menuObj, properties.menuItemName, properties.shortcutKeySequence, + MenuScriptingInterface::getInstance(), SLOT(menuItemTriggered()), + QAction::NoRole, requestedPosition); + } + if (shortcut && menuItemAction) { + connect(shortcut, SIGNAL(activated()), menuItemAction, SLOT(trigger())); + } + QMenuBar::repaint(); + } +} + +void Menu::removeMenuItem(const QString& menu, const QString& menuitem) { + MenuWrapper* menuObj = getMenu(menu); + if (menuObj) { + removeAction(menuObj, menuitem); + QMenuBar::repaint(); + } +}; + +bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { + QAction* menuItemAction = _actionHash.value(menuitem); + if (menuItemAction) { + return (getMenu(menu) != NULL); + } + return false; +}; + + +MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { + VrMenu::instance()->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::instance()->addAction(_realMenu, action); +} + +QAction* MenuWrapper::addAction(const QString& menuName) { + QAction* action = _realMenu->addAction(menuName); + VrMenu::instance()->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::instance()->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::instance()->insertAction(before, action); +} + +QHash MenuWrapper::_backMap; diff --git a/interface/src/Menu.h b/interface/src/Menu.h index d23d7658d0..194f64ddc7 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -18,77 +18,114 @@ #include #include #include -#include + #include -#include - #include "DiscoverabilityManager.h" -#include class Settings; -// Proxy object to simplify porting over -class HifiAction { - const QString _menuOption; +class MenuWrapper : public QObject { public: - HifiAction(const QString & menuOption); - void setCheckable(bool); - void setChecked(bool); - void setVisible(bool); - QString shortcut() const; - void setText(const QString &); - void setTriggerAction(std::function); - void setToggleAction(std::function); + 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 HifiMenu { +class Menu : public QMenuBar { Q_OBJECT - public: - Menu(QQuickItem * parent = 0); static Menu* getInstance(); - - // Override the base type HifiMenu with this class instead - static void registerType() { - qmlRegisterType("Hifi", 1, 0, NAME.toLocal8Bit().constData()); - } - + void loadSettings(); void saveSettings(); + + MenuWrapper* getMenu(const QString& menuName); - HifiAction * getActionForOption(const QString& menuOption) { - return new HifiAction(menuOption); - } - + void triggerOption(const QString& menuOption); + QAction* getActionForOption(const QString& menuOption); + + 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(MenuWrapper* destinationMenu, + QAction* action, + const QString& actionName = QString(), + const QKeySequence& shortcut = 0, + QAction::MenuRole role = QAction::NoRole, + int menuItemLocation = UNSPECIFIED_POSITION); + + void removeAction(MenuWrapper* menu, const QString& actionName); + +public slots: + MenuWrapper* addMenu(const QString& menuName); + void removeMenu(const QString& menuName); + bool menuExists(const QString& menuName); + void addSeparator(const QString& menuName, const QString& separatorName); + void removeSeparator(const QString& menuName, const QString& separatorName); void addMenuItem(const MenuItemProperties& properties); - - bool isOptionChecked(const QString& menuOption) const { - return HifiMenu::isChecked(menuOption); - } - - void setIsOptionChecked(const QString& menuOption, bool isChecked) { - HifiMenu::setChecked(menuOption, isChecked); - } - - void triggerOption(const QString& menuOption) { - HifiMenu::triggerItem(menuOption); - } - - void setOptionText(const QString& menuOption, const QString & text) { - HifiMenu::setItemText(menuOption, text); - } - - void setOptionTriggerAction(const QString& menuOption, std::function f) { - HifiMenu::setTriggerAction(menuOption, f); - } - -private: - void init(); - + void removeMenuItem(const QString& menuName, const QString& menuitem); + bool menuItemExists(const QString& menuName, const QString& menuitem); + bool isOptionChecked(const QString& menuOption) const; + void setIsOptionChecked(const QString& menuOption, bool isChecked); + private: static Menu* _instance; - friend class HifiAction; + Menu(); + + typedef void(*settingsAction)(Settings&, QAction&); + static void loadAction(Settings& settings, QAction& action); + static void saveAction(Settings& settings, QAction& action); + void scanMenuBar(settingsAction modifySetting); + void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings); + + /// helper method to have separators with labels that are also compatible with OS X + void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName, + int menuItemLocation = UNSPECIFIED_POSITION); + + QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu, + const QString& actionName, + const QKeySequence& shortcut = 0, + const bool checked = false, + const QObject* receiver = NULL, + const char* member = NULL, + int menuItemLocation = UNSPECIFIED_POSITION); + + 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(MenuWrapper* menu, const QString& searchMenuItem); + int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition); + + QHash _actionHash; }; namespace MenuOption { @@ -243,4 +280,5 @@ namespace MenuOption { const QString VisibleToNoOne = "No one"; const QString Wireframe = "Wireframe"; } + #endif // hifi_Menu_h diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index 043d5bf40d..7d719873f4 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -216,7 +216,7 @@ void OculusManager::connect() { _isConnected = false; // we're definitely not in "VR mode" so tell the menu that - Menu::getInstance()->setIsOptionChecked(MenuOption::EnableVRMode, false); + Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false); } } diff --git a/interface/src/scripting/MenuScriptingInterface.cpp b/interface/src/scripting/MenuScriptingInterface.cpp index 823bb9f8d0..277c611f04 100644 --- a/interface/src/scripting/MenuScriptingInterface.cpp +++ b/interface/src/scripting/MenuScriptingInterface.cpp @@ -28,50 +28,72 @@ void MenuScriptingInterface::menuItemTriggered() { } void MenuScriptingInterface::addMenu(const QString& menu) { - Menu::getInstance()->addMenu("", menu); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenu", Q_ARG(const QString&, menu)); } void MenuScriptingInterface::removeMenu(const QString& menu) { - Menu::getInstance()->removeMenu(menu); + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu)); } bool MenuScriptingInterface::menuExists(const QString& menu) { - return Menu::getInstance()->menuExists(menu); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menu)); + return result; } void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) { - Menu::getInstance()->addSeparator(menuName, separatorName); + QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator", + Q_ARG(const QString&, menuName), + Q_ARG(const QString&, separatorName)); } void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) { - Menu::getInstance()->removeSeparator(menuName, separatorName); + QMetaObject::invokeMethod(Menu::getInstance(), "removeSeparator", + Q_ARG(const QString&, menuName), + Q_ARG(const QString&, separatorName)); } void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) { - Menu::getInstance()->addMenuItem(properties); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) { - Menu::getInstance()->addItem(menu, menuitem); - Menu::getInstance()->setItemShortcut(menuitem, shortcutKey); + MenuItemProperties properties(menu, menuitem, shortcutKey); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) { - Menu::getInstance()->addItem(menu, menuitem); + MenuItemProperties properties(menu, menuitem); + QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties)); } void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) { - Menu::getInstance()->removeItem(menuitem); + QMetaObject::invokeMethod(Menu::getInstance(), "removeMenuItem", + Q_ARG(const QString&, menu), + Q_ARG(const QString&, menuitem)); }; bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { - return Menu::getInstance()->itemExists(menu, menuitem); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menu), + Q_ARG(const QString&, menuitem)); + return result; } bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { - return Menu::getInstance()->isChecked(menuOption); + bool result; + QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection, + Q_RETURN_ARG(bool, result), + Q_ARG(const QString&, menuOption)); + return result; } void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { - Menu::getInstance()->setChecked(menuOption, isChecked); + QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection, + Q_ARG(const QString&, menuOption), + Q_ARG(bool, isChecked)); } diff --git a/interface/src/scripting/WindowScriptingInterface.cpp b/interface/src/scripting/WindowScriptingInterface.cpp index 5e747f216d..bd2903863d 100644 --- a/interface/src/scripting/WindowScriptingInterface.cpp +++ b/interface/src/scripting/WindowScriptingInterface.cpp @@ -45,6 +45,7 @@ QScriptValue WindowScriptingInterface::hasFocus() { } void WindowScriptingInterface::setFocus() { + // It's forbidden to call focus() from another thread. Application::getInstance()->postLambdaEvent([] { auto window = Application::getInstance()->getWindow(); window->activateWindow(); diff --git a/interface/src/ui/HMDToolsDialog.cpp b/interface/src/ui/HMDToolsDialog.cpp index cedb26fd9b..4a899a641e 100644 --- a/interface/src/ui/HMDToolsDialog.cpp +++ b/interface/src/ui/HMDToolsDialog.cpp @@ -8,7 +8,6 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include #include #include diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 5922d9f3bb..b452f153f0 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -25,18 +25,19 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _root } void LoginDialog::toggleAction() { - AccountManager & accountManager = AccountManager::getInstance(); - Menu* menu = Menu::getInstance(); + AccountManager& accountManager = AccountManager::getInstance(); + QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login); + Q_CHECK_PTR(loginAction); + disconnect(loginAction, 0, 0, 0); + if (accountManager.isLoggedIn()) { // change the menu item to logout - menu->setOptionText(MenuOption::Login, "Logout " + accountManager.getAccountInfo().getUsername()); - menu->setOptionTriggerAction(MenuOption::Login, [] { - AccountManager::getInstance().logout(); - }); + loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername()); + connect(loginAction, &QAction::triggered, &accountManager, &AccountManager::logout); } else { // change the menu item to login - menu->setOptionText(MenuOption::Login, "Login"); - menu->setOptionTriggerAction(MenuOption::Login, [] { + loginAction->setText("Login"); + connect(loginAction, &QAction::triggered, [] { LoginDialog::show(); }); } diff --git a/interface/src/ui/RunningScriptsWidget.cpp b/interface/src/ui/RunningScriptsWidget.cpp index 4c8d29d84c..e1fb0ce481 100644 --- a/interface/src/ui/RunningScriptsWidget.cpp +++ b/interface/src/ui/RunningScriptsWidget.cpp @@ -44,9 +44,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) : connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, this, &RunningScriptsWidget::selectFirstInList); - // FIXME - // 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); @@ -162,7 +162,8 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) { QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; + int menuBarHeight = Menu::getInstance()->geometry().height(); + int topMargin = titleBarHeight + menuBarHeight; setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin, size().width(), parentWidget()->height() - topMargin); diff --git a/interface/src/ui/ToolWindow.cpp b/interface/src/ui/ToolWindow.cpp index 4edae4f2a4..5888b83f4a 100644 --- a/interface/src/ui/ToolWindow.cpp +++ b/interface/src/ui/ToolWindow.cpp @@ -38,7 +38,8 @@ bool ToolWindow::event(QEvent* event) { QRect mainGeometry = mainWindow->geometry(); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); - int topMargin = titleBarHeight; + int menuBarHeight = Menu::getInstance()->geometry().height(); + int topMargin = titleBarHeight + menuBarHeight; _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, DEFAULT_WIDTH, mainGeometry.height() - topMargin); diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/HifiMenu.cpp index 88c9b48f43..f677fe0aef 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/HifiMenu.cpp @@ -1,47 +1,64 @@ #include "HifiMenu.h" #include +#include -// FIXME can this be made a class member? -static const QString MENU_SUFFIX{ "__Menu" }; +// Binds together a Qt Action or Menu with the QML Menu or MenuItem +class MenuUserData : public QObjectUserData { + static const int USER_DATA_ID; -HIFI_QML_DEF_LAMBDA(HifiMenu, [=](QQmlContext* context, QObject* newItem) { +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); + static_cast(newItem)->setRootMenu(rootMenu); context->setContextProperty("rootMenu", rootMenu); }); -HifiMenu::HifiMenu(QQuickItem* parent) : QQuickItem(parent), _triggerMapper(this), _toggleMapper(this) { +VrMenu* VrMenu::_instance{ nullptr }; + +VrMenu* VrMenu::instance() { + if (!_instance) { + VrMenu::registerType(); + VrMenu::load(); + Q_ASSERT(_instance); + } + return _instance; +} + +VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { + _instance = 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; } +// QML helper functions QObject* addMenu(QObject* parent, const QString & text) { // FIXME add more checking here to ensure no name conflicts QVariant returnedValue; @@ -50,7 +67,7 @@ QObject* addMenu(QObject* parent, const QString & text) { Q_ARG(QVariant, text)); QObject* result = returnedValue.value(); if (result) { - result->setObjectName(text + MENU_SUFFIX); + result->setObjectName(text); } return result; } @@ -67,236 +84,109 @@ QObject* addItem(QObject* parent, const QString& text) { return result; } -const QObject* HifiMenu::findMenuObject(const QString & menuOption) const { +const QObject* VrMenu::findMenuObject(const QString & menuOption) const { if (menuOption.isEmpty()) { return _rootMenu; } - const QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); + const QObject* result = _rootMenu->findChild(menuOption); return result; } -QObject* HifiMenu::findMenuObject(const QString & menuOption) { +QObject* VrMenu::findMenuObject(const QString & menuOption) { if (menuOption.isEmpty()) { return _rootMenu; } - QObject* result = _rootMenu->findChild(menuOption + MENU_SUFFIX); + QObject* result = _rootMenu->findChild(menuOption); 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) { +void VrMenu::setRootMenu(QObject* rootMenu) { _rootMenu = rootMenu; } -void HifiMenu::enableItem(const QString & menuOption, bool enabled) { - QObject* menuItem = findMenuObject(menuOption); - if (!menuItem) { - warn(menuOption); - return; +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); } - menuItem->setProperty("enabled", QVariant::fromValue(enabled)); + QObject* result = ::addMenu(qmlParent, menu->title()); + new MenuUserData(menu, result); } -void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) { - addItem(parentMenu, menuOption); - setCheckable(menuOption); - if (checked) { - setChecked(menuOption, checked); - } +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 HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function f) { - setToggleAction(menuOption, f); - addCheckableItem(parentMenu, menuOption, checked); +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 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); -} - -void HifiMenu::setExclusiveGroup(const QString& menuOption, const QString& groupName) { - static const QString GROUP_SUFFIX{ "__Group" }; - auto offscreenUi = DependencyManager::get(); - QObject* group = offscreenUi->getRootItem()->findChild(groupName + GROUP_SUFFIX); - if (!group) { - group = offscreenUi->load("ExclusiveGroup.qml"); - Q_ASSERT(group); - } - QObject* menuItem = findMenuObject(menuOption); - bool result = menuItem->setProperty("text", QVariant::fromValue(group)); +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); } -bool HifiMenu::connectAction(int action, QObject * receiver, const char * slot) { - auto offscreenUi = DependencyManager::get(); - QObject* rootMenu = offscreenUi->getRootItem()->findChild("AllActions"); - QString name = "HifiAction_" + QVariant(action).toString(); - QObject* quitAction = rootMenu->findChild(name); - connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); - return true; +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/HifiMenu.h b/libraries/ui/src/HifiMenu.h index 501baa3cc0..c8e555d464 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -16,67 +16,32 @@ #include #include #include +#include +#include #include "OffscreenUi.h" -class HifiMenu : public QQuickItem { +class VrMenu : public QQuickItem { Q_OBJECT HIFI_QML_DECL_LAMBDA public: - - static bool connectAction(int action, QObject * receiver, const char * slot); - - 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); + static VrMenu* instance(); + 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); -private slots: - void onTriggeredByName(const QString& name); - void onToggledByName(const QString& name); - protected: - QHash> _triggerActions; - QHash> _toggleActions; + QObject* _rootMenu{ nullptr }; + QObject* findMenuObject(const QString& name); const QObject* findMenuObject(const QString& name) const; - QObject* _rootMenu{ nullptr }; - QSignalMapper _triggerMapper; - QSignalMapper _toggleMapper; + + static VrMenu* _instance; + friend class MenuUserData; }; #endif // hifi_MenuConstants_h diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 052a6aac4e..563beb0cc8 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -366,10 +366,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(); @@ -429,6 +433,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); } if (item) { + qDebug() << "Turning item " << !item->isEnabled(); item->setEnabled(!item->isEnabled()); } } diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 37e229d571..192bfd928b 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -320,7 +320,7 @@ public: glDisable(GL_DEPTH_TEST); MessageDialog::registerType(); - HifiMenu::registerType(); + VrMenu::registerType(); qmlRegisterType("Hifi", 1, 0, "MenuConstants"); @@ -348,10 +348,9 @@ public: #else offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->load(QUrl("Menu.qml")); + offscreenUi->load(QUrl("InterfaceMenu.qml")); // Requires a root menu to have been loaded before it can load - HifiMenu::load(); - HifiMenu::connectAction(MenuConstants::Quit, qApp, SLOT(quit())); + VrMenu::load(); #endif installEventFilter(offscreenUi.data()); offscreenUi->resume(); @@ -407,10 +406,6 @@ protected: 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: @@ -433,7 +428,7 @@ protected: QQmlContext* menuContext{ nullptr }; void keyReleaseEvent(QKeyEvent *event) { if (_altPressed && Qt::Key_Alt == event->key()) { - HifiMenu::toggle(); + VrMenu::toggle(); } } From d4d469b88114ea5dd922db27e8a7dbbedc8f098d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Sat, 25 Apr 2015 20:13:59 -0700 Subject: [PATCH 15/33] Adding note to change filename --- libraries/ui/src/HifiMenu.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/HifiMenu.h index 6e133bd54c..43af05a426 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/HifiMenu.h @@ -20,6 +20,7 @@ #include #include "OffscreenUi.h" +// FIXME rename the compilation files to VrMenu.h and VrMenu.cpp after upstream pull requests are merged. class VrMenu : public QQuickItem { Q_OBJECT HIFI_QML_DECL_LAMBDA From 7a222dff1d7fc92f58768e9da574b761316b994b Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 27 Apr 2015 09:54:58 -0700 Subject: [PATCH 16/33] move one-liner isEnabled() to header --- libraries/physics/src/DynamicCharacterController.cpp | 4 ---- libraries/physics/src/DynamicCharacterController.h | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/libraries/physics/src/DynamicCharacterController.cpp b/libraries/physics/src/DynamicCharacterController.cpp index fa1a2d42d1..0bd363dd1f 100644 --- a/libraries/physics/src/DynamicCharacterController.cpp +++ b/libraries/physics/src/DynamicCharacterController.cpp @@ -257,10 +257,6 @@ void DynamicCharacterController::setEnabled(bool enabled) { } } -bool DynamicCharacterController::isEnabled() const { - return _enabled && _dynamicsWorld; -} - void DynamicCharacterController::setDynamicsWorld(btDynamicsWorld* world) { if (_dynamicsWorld != world) { if (_dynamicsWorld) { diff --git a/libraries/physics/src/DynamicCharacterController.h b/libraries/physics/src/DynamicCharacterController.h index bd71a8bcdc..06ff555f66 100644 --- a/libraries/physics/src/DynamicCharacterController.h +++ b/libraries/physics/src/DynamicCharacterController.h @@ -78,7 +78,8 @@ public: bool needsRemoval() const; bool needsAddition() const; void setEnabled(bool enabled); - bool isEnabled() const; + bool isEnabled() const { return _enabled && _dynamicsWorld; } + void setDynamicsWorld(btDynamicsWorld* world); void setLocalBoundingBox(const glm::vec3& corner, const glm::vec3& scale); From 0b1df891af1de70df9838de1aef1fbcde76afc9f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 10:43:20 -0700 Subject: [PATCH 17/33] Backporting visual work from branch --- interface/resources/qml/LoginDialog.qml | 9 +- interface/resources/qml/MessageDialog.qml | 2 - interface/resources/qml/VrMenu.qml | 127 ++++++++++------- interface/resources/qml/controls/Button.qml | 3 +- interface/resources/qml/controls/CheckBox.qml | 16 +++ interface/resources/qml/controls/Dialog.qml | 129 ++++++------------ .../resources/qml/controls/DialogBase.qml | 74 ++++++++++ .../resources/qml/controls/FontAwesome.qml | 4 +- .../resources/qml/controls/IconButton.qml | 24 ---- .../resources/qml/controls/MenuButton.qml | 5 - interface/resources/qml/controls/Slider.qml | 8 ++ interface/resources/qml/controls/Spacer.qml | 11 ++ interface/resources/qml/controls/SpinBox.qml | 15 ++ interface/resources/qml/controls/Text.qml | 6 +- .../resources/qml/controls/TextAndSlider.qml | 24 ++++ .../resources/qml/controls/TextAndSpinBox.qml | 26 ++++ interface/resources/qml/controls/TextArea.qml | 8 +- interface/resources/qml/controls/TextEdit.qml | 6 +- .../resources/qml/controls/TextHeader.qml | 9 ++ .../resources/qml/controls/TextInput.qml | 44 +++--- .../qml/controls/TextInputAndButton.qml | 40 ++++++ interface/resources/qml/styles/Border.qml | 13 +- .../resources/qml/styles/ButtonStyle.qml | 5 +- .../resources/qml/styles/HifiConstants.qml | 61 +++++++++ .../resources/qml/styles/HifiPalette.qml | 10 +- .../resources/qml/styles/IconButtonStyle.qml | 11 +- .../resources/qml/styles/MenuButtonStyle.qml | 22 --- 27 files changed, 466 insertions(+), 246 deletions(-) create mode 100644 interface/resources/qml/controls/CheckBox.qml create mode 100644 interface/resources/qml/controls/DialogBase.qml delete mode 100644 interface/resources/qml/controls/IconButton.qml delete mode 100644 interface/resources/qml/controls/MenuButton.qml create mode 100644 interface/resources/qml/controls/Slider.qml create mode 100644 interface/resources/qml/controls/Spacer.qml create mode 100644 interface/resources/qml/controls/SpinBox.qml create mode 100644 interface/resources/qml/controls/TextAndSlider.qml create mode 100644 interface/resources/qml/controls/TextAndSpinBox.qml create mode 100644 interface/resources/qml/controls/TextHeader.qml create mode 100644 interface/resources/qml/controls/TextInputAndButton.qml create mode 100644 interface/resources/qml/styles/HifiConstants.qml delete mode 100644 interface/resources/qml/styles/MenuButtonStyle.qml diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index b3b926bbe3..1e02683c57 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 @@ -117,7 +116,7 @@ Dialog { width: 192 height: 64 anchors.horizontalCenter: parent.horizontalCenter - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue border.width: 0 radius: 10 @@ -160,7 +159,7 @@ Dialog { text:"Create Account" font.pointSize: 12 font.bold: true - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue MouseArea { anchors.fill: parent @@ -177,7 +176,7 @@ Dialog { verticalAlignment: Text.AlignVCenter font.pointSize: 12 text: "Recover Password" - color: hifiPalette.hifiBlue + color: hifi.colors.hifiBlue MouseArea { anchors.fill: parent diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index f469e33c30..a2a5ba9304 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -18,8 +18,6 @@ Dialog { onImplicitHeightChanged: root.height = implicitHeight onImplicitWidthChanged: root.width = implicitWidth - SystemPalette { id: palette } - function calculateImplicitWidth() { if (buttons.visibleChildren.length < 2) return; diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 8c4aaafb3b..7de66de23c 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -7,12 +7,19 @@ import "styles" Hifi.VrMenu { id: root + HifiConstants { id: hifi } + anchors.fill: parent + objectName: "VrMenu" + enabled: false opacity: 0.0 + property int animationDuration: 200 - HifiPalette { id: hifiPalette } + property var models: [] + property var columns: [] + z: 10000 onEnabledChanged: { @@ -22,7 +29,7 @@ Hifi.VrMenu { opacity = enabled ? 1.0 : 0.0 if (enabled) { forceActiveFocus() - } + } } // The actual animator @@ -41,10 +48,6 @@ Hifi.VrMenu { if (!visible) reset(); } - - property var models: [] - property var columns: [] - property var menuBuilder: Component { Border { Component.onCompleted: { @@ -58,10 +61,20 @@ Hifi.VrMenu { y = lastMousePosition.y - height / 2; } } - SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } - border.color: hifiPalette.hifiBlue - color: sysPalette.window + border.color: hifi.colors.hifiBlue + color: hifi.colors.window property int menuDepth +/* + MouseArea { +// Rectangle { anchors.fill: parent; color: "#7f0000FF"; visible: enabled } + anchors.fill: parent + onClicked: { + while (parent.menuDepth != root.models.length - 1) { + root.popColumn() + } + } + } +*/ ListView { spacing: 6 @@ -71,12 +84,11 @@ Hifi.VrMenu { anchors.margins: outerMargin id: listView height: root.height - currentIndex: -1 onCountChanged: { recalculateSize() - } - + } + function recalculateSize() { var newHeight = 0 var newWidth = minWidth; @@ -87,11 +99,11 @@ Hifi.VrMenu { parent.height = newHeight + outerMargin * 2; parent.width = newWidth + outerMargin * 2 } - + highlight: Rectangle { width: listView.minWidth - 32; height: 32 - color: sysPalette.highlight + color: hifi.colors.hifiBlue y: (listView.currentItem) ? listView.currentItem.y : 0; x: 32 Behavior on y { @@ -101,10 +113,8 @@ Hifi.VrMenu { } } } - - property int columnIndex: root.models.length - 1 - model: root.models[columnIndex] + model: root.models[menuDepth] delegate: Loader { id: loader sourceComponent: root.itemBuilder @@ -120,12 +130,6 @@ Hifi.VrMenu { 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" @@ -133,26 +137,24 @@ Hifi.VrMenu { when: loader.status == Loader.Ready } } - } - } } - + 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" - + color: source.enabled ? hifi.colors.text : hifi.colors.disabledText + enabled: source.enabled + + onImplicitWidthChanged: { if (listView) { listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); @@ -163,15 +165,27 @@ Hifi.VrMenu { FontAwesome { visible: source.type == 1 && source.checkable x: -32 - text: (source.type == 1 && source.checked) ? "\uF05D" : "\uF10C" + text: checkText(); + color: parent.color + function checkText() { + if (source.type != 1) { + 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" + } } - + FontAwesome { visible: source.type == 2 - x: listView.width - 64 + x: listView.width - 32 - (hifi.layout.spacing * 2) text: "\uF0DA" + color: parent.color } - function typedText() { switch(source.type) { @@ -200,38 +214,53 @@ Hifi.VrMenu { interval: 1000 onTriggered: parent.select(); } - onEntered: { - if (source.type == 2 && enabled) { - timer.start() - } - } - onExited: { - timer.stop() - } + /* + * 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 + * + */ + //onEntered: { + // if (source.type == 2 && enabled) { + // timer.start() + // } + //} + //onExited: { + // timer.stop() + //} onClicked: { select(); } function select() { timer.stop(); - listView.currentIndex = listViewIndex - parent.root.selectItem(parent.source); + 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; - oldColumn.opacity = 0.5; + //oldColumn.enabled = false } var newColumn = menuBuilder.createObject(root); columns.push(newColumn); 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..629813d3b5 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,6 +85,7 @@ Item { MouseArea { id: sizeDrag + enabled: root.resizable property int startX property int startY anchors.right: parent.right @@ -103,7 +98,7 @@ Item { startY = mouseY } onPositionChanged: { - if (pressed && root.resizable) { + if (pressed) { root.deltaSize((mouseX - startX), (mouseY - startY)) startX = mouseX startY = mouseY @@ -111,83 +106,41 @@ 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 - + } + } } diff --git a/interface/resources/qml/controls/DialogBase.qml b/interface/resources/qml/controls/DialogBase.qml new file mode 100644 index 0000000000..e5f0a8f0d1 --- /dev/null +++ b/interface/resources/qml/controls/DialogBase.qml @@ -0,0 +1,74 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import "." +import "../styles" + +Item { + id: root + HifiConstants { id: hifi } + implicitHeight: 512 + implicitWidth: 512 + 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 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 + + property alias clientBorder: clientBorder + readonly property real clientX: clientBorder.x + hifi.styles.borderWidth + readonly property real clientY: clientBorder.y + hifi.styles.borderWidth + readonly property real clientWidth: clientBorder.width - hifi.styles.borderWidth * 2 + readonly property real clientHeight: clientBorder.height - hifi.styles.borderWidth * 2 + + /* + * Window decorations, with a title bar and frames + */ + Border { + id: windowBorder + anchors.fill: parent + border.color: root.frameColor + color: "#00000000" + + Border { + id: titleBorder + height: hifi.layout.windowTitleHeight + anchors.right: parent.right + anchors.left: parent.left + border.color: root.frameColor + 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 + + Border { + id: clientBorder + 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 + clip: true + } // 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..ceaca0c073 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" - } -} From d55d467d3f19e70492e8de3c0886591429744ca4 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 11:35:57 -0700 Subject: [PATCH 18/33] Tweaking VR menu for PR --- interface/resources/qml/VrMenu.qml | 22 +++++++++++++++++-- interface/src/Application.cpp | 9 +++++--- interface/src/Menu.cpp | 2 +- libraries/ui/src/{HifiMenu.cpp => VrMenu.cpp} | 14 +++++++----- libraries/ui/src/{HifiMenu.h => VrMenu.h} | 10 ++++----- tests/ui/src/main.cpp | 2 +- 6 files changed, 42 insertions(+), 17 deletions(-) rename libraries/ui/src/{HifiMenu.cpp => VrMenu.cpp} (93%) rename libraries/ui/src/{HifiMenu.h => VrMenu.h} (83%) diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 7de66de23c..01714e9ebe 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -94,6 +94,9 @@ Hifi.VrMenu { var newWidth = minWidth; for (var i = 0; i < children.length; ++i) { var item = children[i]; + if (!item.visible) { + continue + } newHeight += item.height } parent.height = newHeight + outerMargin * 2; @@ -152,8 +155,23 @@ Hifi.VrMenu { height: implicitHeight width: implicitWidth color: source.enabled ? hifi.colors.text : hifi.colors.disabledText - enabled: source.enabled - + enabled: source.enabled && source.visible + // FIXME uncommenting this line results in menus that have blank spots + // rather than having the correct size + // visible: source.visible + + onListViewChanged: { + if (listView) { + listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); + listView.recalculateSize(); + } + } + + onVisibleChanged: { + if (listView) { + listView.recalculateSize(); + } + } onImplicitWidthChanged: { if (listView) { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 5fd3cc48ab..2550651a34 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -63,7 +63,7 @@ #include #include #include -#include +#include #include #include #include @@ -1327,13 +1327,16 @@ 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()) { -#ifndef DEBUG +#ifdef VR_MENU_ONLY_IN_HMD if (OculusManager::isConnected()) { #endif VrMenu::toggle(); -#ifndef DEBUG +#ifdef VR_MENU_ONLY_IN_HMD } #endif } diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 67fae46b33..42670c2979 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include #include "Application.h" #include "AccountManager.h" diff --git a/libraries/ui/src/HifiMenu.cpp b/libraries/ui/src/VrMenu.cpp similarity index 93% rename from libraries/ui/src/HifiMenu.cpp rename to libraries/ui/src/VrMenu.cpp index 854f0d6eb2..e0fa296857 100644 --- a/libraries/ui/src/HifiMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -1,5 +1,5 @@ // -// HifiMenu.cpp +// VrMenu.cpp // // Created by Bradley Austin Davis on 2015/04/21 // Copyright 2015 High Fidelity, Inc. @@ -8,11 +8,15 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -#include "HifiMenu.h" +#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; @@ -69,7 +73,7 @@ VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { } // QML helper functions -QObject* addMenu(QObject* parent, const QString & text) { +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, @@ -94,7 +98,7 @@ QObject* addItem(QObject* parent, const QString& text) { return result; } -const QObject* VrMenu::findMenuObject(const QString & menuOption) const { +const QObject* VrMenu::findMenuObject(const QString& menuOption) const { if (menuOption.isEmpty()) { return _rootMenu; } @@ -102,7 +106,7 @@ const QObject* VrMenu::findMenuObject(const QString & menuOption) const { return result; } -QObject* VrMenu::findMenuObject(const QString & menuOption) { +QObject* VrMenu::findMenuObject(const QString& menuOption) { if (menuOption.isEmpty()) { return _rootMenu; } diff --git a/libraries/ui/src/HifiMenu.h b/libraries/ui/src/VrMenu.h similarity index 83% rename from libraries/ui/src/HifiMenu.h rename to libraries/ui/src/VrMenu.h index 43af05a426..7fe0d33cc4 100644 --- a/libraries/ui/src/HifiMenu.h +++ b/libraries/ui/src/VrMenu.h @@ -1,5 +1,5 @@ // -// HifiMenu.h +// VrMenu.h // // Created by Bradley Austin Davis on 2015/04/21 // Copyright 2015 High Fidelity, Inc. @@ -9,8 +9,8 @@ // #pragma once -#ifndef hifi_HifiMenu_h -#define hifi_HifiMenu_h +#ifndef hifi_VrMenu_h +#define hifi_VrMenu_h #include #include @@ -20,7 +20,7 @@ #include #include "OffscreenUi.h" -// FIXME rename the compilation files to VrMenu.h and VrMenu.cpp after upstream pull requests are merged. +// 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 @@ -45,4 +45,4 @@ protected: friend class MenuUserData; }; -#endif // hifi_HifiMenu_h +#endif // hifi_VrMenu_h diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index 192bfd928b..ae8375717e 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; From 76ce34a74b3d4243d5a8d2b17c12f43e4e2422ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 11:41:10 -0700 Subject: [PATCH 19/33] Scanning for code style --- interface/src/ui/MarketplaceDialog.cpp | 4 ++-- interface/src/ui/MarketplaceDialog.h | 4 ++-- libraries/ui/src/VrMenu.cpp | 2 +- tests/ui/src/main.cpp | 18 +++++++++--------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/interface/src/ui/MarketplaceDialog.cpp b/interface/src/ui/MarketplaceDialog.cpp index f37f71ef54..b9c640054c 100644 --- a/interface/src/ui/MarketplaceDialog.cpp +++ b/interface/src/ui/MarketplaceDialog.cpp @@ -15,10 +15,10 @@ HIFI_QML_DEF(MarketplaceDialog) -MarketplaceDialog::MarketplaceDialog(QQuickItem *parent) : OffscreenQmlDialog(parent) { +MarketplaceDialog::MarketplaceDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) { } -bool MarketplaceDialog::navigationRequested(const QString & url) { +bool MarketplaceDialog::navigationRequested(const QString& url) { qDebug() << url; if (Application::getInstance()->canAcceptURL(url)) { if (Application::getInstance()->acceptURL(url)) { diff --git a/interface/src/ui/MarketplaceDialog.h b/interface/src/ui/MarketplaceDialog.h index 241d4a7131..2440c3e07c 100644 --- a/interface/src/ui/MarketplaceDialog.h +++ b/interface/src/ui/MarketplaceDialog.h @@ -20,9 +20,9 @@ class MarketplaceDialog : public OffscreenQmlDialog HIFI_QML_DECL public: - MarketplaceDialog(QQuickItem *parent = 0); + MarketplaceDialog(QQuickItem* parent = nullptr); - Q_INVOKABLE bool navigationRequested(const QString & url); + Q_INVOKABLE bool navigationRequested(const QString& url); }; diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index e0fa296857..04c2cb0b30 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -50,7 +50,7 @@ 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"); + QObject* rootMenu = offscreenUi->getRootItem()->findChild("rootMenu"); Q_ASSERT(rootMenu); static_cast(newItem)->setRootMenu(rootMenu); context->setContextProperty("rootMenu", rootMenu); diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index ae8375717e..aaca2efa2a 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -234,12 +234,12 @@ public: }; public: - MenuConstants(QObject * parent = nullptr) : QObject(parent) { + MenuConstants(QObject* parent = nullptr) : QObject(parent) { } }; -const QString & getQmlDir() { +const QString& getQmlDir() { static QString dir; if (dir.isEmpty()) { QDir path(__FILE__); @@ -250,7 +250,7 @@ const QString & getQmlDir() { return dir; } -const QString & getTestQmlDir() { +const QString& getTestQmlDir() { static QString dir; if (dir.isEmpty()) { QDir path(__FILE__); @@ -265,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; @@ -273,7 +273,7 @@ class QTestWindow : public QWindow, private QOpenGLFunctions { int testQmlTexture{ 0 }; public: - QObject * rootMenu; + QObject* rootMenu; QTestWindow() { _timer.setInterval(1); @@ -304,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) { @@ -396,12 +396,12 @@ 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: @@ -432,7 +432,7 @@ protected: } } - void moveEvent(QMoveEvent *event) { + void moveEvent(QMoveEvent* event) { static qreal oldPixelRatio = 0.0; if (devicePixelRatio() != oldPixelRatio) { oldPixelRatio = devicePixelRatio(); From ca16d37ce5fe70dd62cc24eafb5049a781bba73e Mon Sep 17 00:00:00 2001 From: Bradley Austin Davis Date: Mon, 27 Apr 2015 12:46:34 -0700 Subject: [PATCH 20/33] Mac testing --- interface/resources/qml/VrMenu.qml | 42 +++++++++++++++++------------- libraries/ui/src/OffscreenUi.cpp | 2 +- tests/render-utils/src/main.cpp | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 01714e9ebe..4bbcb9c498 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -108,7 +108,7 @@ Hifi.VrMenu { height: 32 color: hifi.colors.hifiBlue y: (listView.currentItem) ? listView.currentItem.y : 0; - x: 32 + x: (listView.currentItem) ? listView.currentItem.height : 0; Behavior on y { NumberAnimation { duration: 100 @@ -132,6 +132,12 @@ Hifi.VrMenu { 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 @@ -147,10 +153,11 @@ Hifi.VrMenu { property var itemBuilder: Component { Text { id: thisText - x: 32 + x: height property var source property var root property var listView + property var border text: typedText() height: implicitHeight width: implicitWidth @@ -167,12 +174,6 @@ Hifi.VrMenu { } } - onVisibleChanged: { - if (listView) { - listView.recalculateSize(); - } - } - onImplicitWidthChanged: { if (listView) { listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); @@ -182,12 +183,13 @@ Hifi.VrMenu { FontAwesome { visible: source.type == 1 && source.checkable - x: -32 + x: -parent.height + size: parent.height text: checkText(); color: parent.color function checkText() { - if (source.type != 1) { - return; + if (!source || source.type != 1) { + return ""; } // FIXME this works for native QML menus but I don't think it will // for proxied QML menus @@ -199,6 +201,7 @@ Hifi.VrMenu { } FontAwesome { + size: parent.height visible: source.type == 2 x: listView.width - 32 - (hifi.layout.spacing * 2) text: "\uF0DA" @@ -206,14 +209,17 @@ Hifi.VrMenu { } function typedText() { - switch(source.type) { - case 2: - return source.title; - case 1: - return source.text; - case 0: - return "-----" + if (source) { + switch(source.type) { + case 2: + return source.title; + case 1: + return source.text; + case 0: + return "-----" + } } + return "" } MouseArea { diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 5db7349a02..8ba7b514e4 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) { 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) { From fd28baac8c713446163a7b1e5e877e709e3c90ef Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 15:42:20 -0700 Subject: [PATCH 21/33] Working on message box and addressbar tweaks --- interface/resources/qml/AddressBarDialog.qml | 31 ++++++---- interface/resources/qml/MessageDialog.qml | 60 +++++++------------ .../resources/qml/controls/DialogBase.qml | 6 +- 3 files changed, 45 insertions(+), 52 deletions(-) diff --git a/interface/resources/qml/AddressBarDialog.qml b/interface/resources/qml/AddressBarDialog.qml index 8d2439e5b8..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,24 +38,26 @@ 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 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/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index a2a5ba9304..02f1e4716f 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -3,71 +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 - - 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 @@ -83,7 +63,7 @@ Dialog { horizontalAlignment: Text.AlignLeft color: iconColor() text: iconSymbol() - + function iconSymbol() { switch (content.icon) { case Hifi.MessageDialog.Information: @@ -262,7 +242,6 @@ Dialog { onClicked: content.click(StandardButton.Help) visible: content.standardButtons & StandardButton.Help } - onVisibleChildrenChanged: root.calculateImplicitWidth() } } @@ -319,6 +298,7 @@ Dialog { ] } + Keys.onPressed: { if (event.modifiers === Qt.ControlModifier) switch (event.key) { diff --git a/interface/resources/qml/controls/DialogBase.qml b/interface/resources/qml/controls/DialogBase.qml index e5f0a8f0d1..050237c184 100644 --- a/interface/resources/qml/controls/DialogBase.qml +++ b/interface/resources/qml/controls/DialogBase.qml @@ -6,13 +6,15 @@ import "../styles" Item { id: root HifiConstants { id: hifi } - implicitHeight: 512 - implicitWidth: 512 + 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 From 1beaf18d1baf096bec57aff78c70397fb76d1597 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 16:00:23 -0700 Subject: [PATCH 22/33] Prevent the login dialog from spawning the address bar on enter keys --- interface/resources/qml/LoginDialog.qml | 28 ++++++++++++++++--------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 1e02683c57..5653dfc7a1 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -60,9 +60,6 @@ Dialog { anchors.margins: 8 KeyNavigation.tab: password KeyNavigation.backtab: password - onAccepted: { - password.forceActiveFocus() - } } } @@ -78,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() @@ -187,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; + } + } } From 10b59350630553a500c912aa9d57f23c6a303787 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 18:11:19 -0700 Subject: [PATCH 23/33] Let QML take care of more of the height/width stuff --- interface/resources/qml/VrMenu.qml | 335 +++++++++++++---------------- 1 file changed, 152 insertions(+), 183 deletions(-) diff --git a/interface/resources/qml/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 4bbcb9c498..01a726d2ba 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -50,6 +50,7 @@ Hifi.VrMenu { property var menuBuilder: Component { Border { + HifiConstants { id: hifi } Component.onCompleted: { menuDepth = root.models.length - 1 if (menuDepth == 0) { @@ -64,213 +65,181 @@ Hifi.VrMenu { border.color: hifi.colors.hifiBlue color: hifi.colors.window property int menuDepth -/* - MouseArea { -// Rectangle { anchors.fill: parent; color: "#7f0000FF"; visible: enabled } - anchors.fill: parent - onClicked: { - while (parent.menuDepth != root.models.length - 1) { - root.popColumn() - } - } - } -*/ + implicitHeight: listView.implicitHeight + 16 + implicitWidth: listView.implicitWidth + 16 - ListView { - spacing: 6 - property int outerMargin: 8 - property real minWidth: 0 - anchors.fill: parent - anchors.margins: outerMargin + Column { id: listView - height: root.height - - onCountChanged: { - recalculateSize() + property real minWidth: 0 + anchors { + top: parent.top + topMargin: 8 + left: parent.left + leftMargin: 8 + right: parent.right + rightMargin: 8 } - function recalculateSize() { - var newHeight = 0 - var newWidth = minWidth; - for (var i = 0; i < children.length; ++i) { - var item = children[i]; - if (!item.visible) { - continue - } - newHeight += item.height + 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 + } } - parent.height = newHeight + outerMargin * 2; - parent.width = newWidth + outerMargin * 2 - } - - highlight: Rectangle { - width: listView.minWidth - 32; - height: 32 - color: hifi.colors.hifiBlue - y: (listView.currentItem) ? listView.currentItem.y : 0; - x: (listView.currentItem) ? listView.currentItem.height : 0; - Behavior on y { - NumberAnimation { - duration: 100 - easing.type: Easing.InOutQuint - } - } - } - - 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 { - Text { - id: thisText - x: height + Item { property var source property var root property var listView property var border - text: typedText() - height: implicitHeight - width: implicitWidth - color: source.enabled ? hifi.colors.text : hifi.colors.disabledText - enabled: source.enabled && source.visible + 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 - - onListViewChanged: { - if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); - listView.recalculateSize(); + Row { + id: row + spacing: 4 + anchors { + top: parent.top + topMargin: 2 } - } + FontAwesome { + id: check + size: label.height + text: checkText() + color: label.color + function checkText() { + if (!source || source.type != 1 || !source.checkable) { + return ""; + } - onImplicitWidthChanged: { - if (listView) { - listView.minWidth = Math.max(listView.minWidth, implicitWidth + 64); - listView.recalculateSize(); - } - } - - FontAwesome { - visible: source.type == 1 && source.checkable - x: -parent.height - size: parent.height - text: checkText(); - color: parent.color - function checkText() { - if (!source || source.type != 1) { - 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" - } - } - - FontAwesome { - size: parent.height - visible: source.type == 2 - x: listView.width - 32 - (hifi.layout.spacing * 2) - text: "\uF0DA" - color: parent.color - } - - function typedText() { - if (source) { - switch(source.type) { - case 2: - return source.title; - case 1: - return source.text; - case 0: - 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" } } - return "" - } + 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 { - id: mouseArea - acceptedButtons: Qt.LeftButton - anchors.left: parent.left - anchors.leftMargin: -32 - anchors.bottom: parent.bottom - anchors.bottomMargin: 0 - anchors.top: parent.top - anchors.topMargin: 0 - width: listView.width - hoverEnabled: true - Timer { - id: timer - interval: 1000 - onTriggered: parent.select(); - } - /* - * 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 - * - */ - //onEntered: { - // if (source.type == 2 && enabled) { - // timer.start() - // } - //} - //onExited: { - // timer.stop() - //} - onClicked: { - select(); - } - function select() { - timer.stop(); - var popped = false; - while (columns.length - 1 > listView.parent.menuDepth) { - popColumn(); - popped = true; - } + MouseArea { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + right: tag.right + } + 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); - } + if (!popped || source.type != 1) { + parent.root.selectItem(parent.source); + } } } } From 19ca543c2c9ab177e5343f88d8595307a00ddd23 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 19:07:53 -0700 Subject: [PATCH 24/33] Cleaning up code and tweaking menu layout --- interface/resources/qml/InterfaceMenu.qml | 1073 --------------------- interface/resources/qml/MessageDialog.qml | 1 - interface/resources/qml/TestMenu.qml | 115 +++ interface/resources/qml/VrMenu.qml | 17 +- libraries/ui/src/OffscreenUi.cpp | 1 - tests/ui/src/main.cpp | 6 +- 6 files changed, 128 insertions(+), 1085 deletions(-) delete mode 100644 interface/resources/qml/InterfaceMenu.qml create mode 100644 interface/resources/qml/TestMenu.qml diff --git a/interface/resources/qml/InterfaceMenu.qml b/interface/resources/qml/InterfaceMenu.qml deleted file mode 100644 index 2a5ed8d212..0000000000 --- a/interface/resources/qml/InterfaceMenu.qml +++ /dev/null @@ -1,1073 +0,0 @@ -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" - } - - // Scripts - Action { - id: loadScript - objectName: "HifiAction_" + MenuConstants.LoadScript - text: qsTr("Open and Run Script File...") - shortcut: "Ctrl+O" - onTriggered: { - console.log("Shortcut ctrl+o hit"); - } - } - Action { - id: loadScriptURL - objectName: "HifiAction_" + MenuConstants.LoadScriptURL - text: qsTr("Open and Run Script from URL...") - shortcut: "Ctrl+Shift+O" - } - Action { - id: reloadAllScripts - objectName: "HifiAction_" + MenuConstants.ReloadAllScripts - text: qsTr("Reload All Scripts") - } - Action { - id: runningScripts - objectName: "HifiAction_" + MenuConstants.RunningScripts - text: qsTr("Running Scripts") - } - Action { - id: stopAllScripts - objectName: "HifiAction_" + MenuConstants.StopAllScripts - text: qsTr("Stop All Scripts") - } - - // Locations - Action { - id: bookmarkLocation - objectName: "HifiAction_" + MenuConstants.BookmarkLocation - text: qsTr("Bookmark Location") - } - Action { - id: bookmarks - objectName: "HifiAction_" + MenuConstants.Bookmarks - text: qsTr("Bookmarks") - } - Action { - id: addressBar - objectName: "HifiAction_" + MenuConstants.AddressBar - text: qsTr("Show Address Bar") - } - - // - // 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 - objectName: "HifiAction_" + MenuConstants.Attachments - text: qsTr("Attachments...") - } - - // - // Tools menu - // - Action { - id: scriptEditor - objectName: "HifiAction_" + MenuConstants.ScriptEditor - text: qsTr("Script Editor...") - } - Action { - id: controlWithSpeech - objectName: "HifiAction_" + MenuConstants.ControlWithSpeech - text: qsTr("Control With Speech") - } - Action { - id: chat - objectName: "HifiAction_" + MenuConstants.Chat - text: qsTr("Chat...") - } - Action { - id: addRemoveFriends - objectName: "HifiAction_" + MenuConstants.AddRemoveFriends - text: qsTr("Add/Remove Friends...") - } - ExclusiveGroup { - Action { - id: visibleToEveryone - objectName: "HifiAction_" + MenuConstants.VisibleToEveryone - text: qsTr("Everyone") - } - Action { - id: visibleToFriends - objectName: "HifiAction_" + MenuConstants.VisibleToFriends - text: qsTr("Friends") - } - Action { - id: visibleToNoOne - objectName: "HifiAction_" + MenuConstants.VisibleToNoOne - text: qsTr("No one") - } - } - Action { - id: toolWindow - objectName: "HifiAction_" + MenuConstants.ToolWindow - text: qsTr("Tool Window") - } - Action { - id: javascriptConsole - objectName: "HifiAction_" + MenuConstants.JavascriptConsole - text: qsTr("Console...") - } - Action { - id: resetSensors - objectName: "HifiAction_" + MenuConstants.ResetSensors - text: qsTr("Reset Sensors") - } - - - - - - - - - - - - - Action { - id: alignForearmsWithWrists - objectName: "HifiAction_" + MenuConstants.AlignForearmsWithWrists - text: qsTr("Align Forearms with Wrists") - checkable: true - } - Action { - id: alternateIK - objectName: "HifiAction_" + MenuConstants.AlternateIK - text: qsTr("Alternate IK") - checkable: true - } - Action { - id: ambientOcclusion - objectName: "HifiAction_" + MenuConstants.AmbientOcclusion - text: qsTr("Ambient Occlusion") - checkable: true - } - Action { - id: atmosphere - objectName: "HifiAction_" + MenuConstants.Atmosphere - text: qsTr("Atmosphere") - } - Action { - id: audioNoiseReduction - objectName: "HifiAction_" + MenuConstants.AudioNoiseReduction - text: qsTr("Audio Noise Reduction") - checkable: true - } - Action { - id: audioScope - objectName: "HifiAction_" + MenuConstants.AudioScope - text: qsTr("Show Scope") - checkable: true - } - ExclusiveGroup { - Action { - id: audioScopeFiveFrames - objectName: "HifiAction_" + MenuConstants.AudioScopeFiveFrames - text: qsTr("Five") - checkable: true - checked: true - } - Action { - id: audioScopeFiftyFrames - objectName: "HifiAction_" + MenuConstants.AudioScopeFiftyFrames - text: qsTr("Fifty") - checkable: true - } - Action { - id: audioScopeTwentyFrames - objectName: "HifiAction_" + MenuConstants.AudioScopeTwentyFrames - text: qsTr("Twenty") - checkable: true - } - } - Action { - id: audioScopePause - objectName: "HifiAction_" + MenuConstants.AudioScopePause - text: qsTr("Pause Scope") - checkable: true - } - Action { - id: audioStats - objectName: "HifiAction_" + MenuConstants.AudioStats - text: qsTr("Audio Stats") - checkable: true - } - Action { - id: audioStatsShowInjectedStreams - objectName: "HifiAction_" + MenuConstants.AudioStatsShowInjectedStreams - text: qsTr("Audio Stats Show Injected Streams") - checkable: true - } - Action { - id: bandwidthDetails - objectName: "HifiAction_" + MenuConstants.BandwidthDetails - text: qsTr("Bandwidth Details") - checkable: true - } - Action { - id: blueSpeechSphere - objectName: "HifiAction_" + MenuConstants.BlueSpeechSphere - text: qsTr("Blue Sphere While Speaking") - checkable: true - } - Action { - id: cascadedShadows - objectName: "HifiAction_" + MenuConstants.CascadedShadows - text: qsTr("Cascaded") - } - Action { - id: cachesSize - objectName: "HifiAction_" + MenuConstants.CachesSize - text: qsTr("RAM Caches Size") - } - Action { - id: collisions - objectName: "HifiAction_" + MenuConstants.Collisions - text: qsTr("Collisions") - } - Action { - id: copyAddress - objectName: "HifiAction_" + MenuConstants.CopyAddress - text: qsTr("Copy Address to Clipboard") - } - Action { - id: copyPath - objectName: "HifiAction_" + MenuConstants.CopyPath - text: qsTr("Copy Path to Clipboard") - } - Action { - id: decreaseAvatarSize - objectName: "HifiAction_" + MenuConstants.DecreaseAvatarSize - text: qsTr("Decrease Avatar Size") - } - Action { - id: deleteBookmark - objectName: "HifiAction_" + MenuConstants.DeleteBookmark - text: qsTr("Delete Bookmark...") - } - Action { - id: disableActivityLogger - objectName: "HifiAction_" + MenuConstants.DisableActivityLogger - text: qsTr("Disable Activity Logger") - } - Action { - id: disableLightEntities - objectName: "HifiAction_" + MenuConstants.DisableLightEntities - text: qsTr("Disable Light Entities") - } - Action { - id: disableNackPackets - objectName: "HifiAction_" + MenuConstants.DisableNackPackets - text: qsTr("Disable NACK Packets") - } - Action { - id: diskCacheEditor - objectName: "HifiAction_" + MenuConstants.DiskCacheEditor - text: qsTr("Disk Cache Editor") - } - Action { - id: displayHands - objectName: "HifiAction_" + MenuConstants.DisplayHands - text: qsTr("Show Hand Info") - } - Action { - id: displayHandTargets - objectName: "HifiAction_" + MenuConstants.DisplayHandTargets - text: qsTr("Show Hand Targets") - } - Action { - id: displayModelBounds - objectName: "HifiAction_" + MenuConstants.DisplayModelBounds - text: qsTr("Display Model Bounds") - } - Action { - id: displayModelTriangles - objectName: "HifiAction_" + MenuConstants.DisplayModelTriangles - text: qsTr("Display Model Triangles") - } - Action { - id: displayModelElementChildProxies - objectName: "HifiAction_" + MenuConstants.DisplayModelElementChildProxies - text: qsTr("Display Model Element Children") - } - Action { - id: displayModelElementProxy - objectName: "HifiAction_" + MenuConstants.DisplayModelElementProxy - text: qsTr("Display Model Element Bounds") - } - Action { - id: displayDebugTimingDetails - objectName: "HifiAction_" + MenuConstants.DisplayDebugTimingDetails - text: qsTr("Display Timing Details") - } - Action { - id: dontDoPrecisionPicking - objectName: "HifiAction_" + MenuConstants.DontDoPrecisionPicking - text: qsTr("Don't Do Precision Picking") - } - Action { - id: dontFadeOnOctreeServerChanges - objectName: "HifiAction_" + MenuConstants.DontFadeOnOctreeServerChanges - text: qsTr("Don't Fade In/Out on Octree Server Changes") - } - Action { - id: dontRenderEntitiesAsScene - objectName: "HifiAction_" + MenuConstants.DontRenderEntitiesAsScene - text: qsTr("Don't Render Entities as Scene") - } - Action { - id: echoLocalAudio - objectName: "HifiAction_" + MenuConstants.EchoLocalAudio - text: qsTr("Echo Local Audio") - } - Action { - id: echoServerAudio - objectName: "HifiAction_" + MenuConstants.EchoServerAudio - text: qsTr("Echo Server Audio") - } - Action { - id: editEntitiesHelp - objectName: "HifiAction_" + MenuConstants.EditEntitiesHelp - text: qsTr("Edit Entities Help...") - } - Action { - id: enable3DTVMode - objectName: "HifiAction_" + MenuConstants.Enable3DTVMode - text: qsTr("Enable 3DTV Mode") - } - Action { - id: enableCharacterController - objectName: "HifiAction_" + MenuConstants.EnableCharacterController - text: qsTr("Enable avatar collisions") - } - Action { - id: enableGlowEffect - objectName: "HifiAction_" + MenuConstants.EnableGlowEffect - text: qsTr("Enable Glow Effect (Warning: Poor Oculus Performance)") - } - Action { - id: enableVRMode - objectName: "HifiAction_" + MenuConstants.EnableVRMode - text: qsTr("Enable VR Mode") - } - Action { - id: expandMyAvatarSimulateTiming - objectName: "HifiAction_" + MenuConstants.ExpandMyAvatarSimulateTiming - text: qsTr("Expand /myAvatar/simulation") - } - Action { - id: expandMyAvatarTiming - objectName: "HifiAction_" + MenuConstants.ExpandMyAvatarTiming - text: qsTr("Expand /myAvatar") - } - Action { - id: expandOtherAvatarTiming - objectName: "HifiAction_" + MenuConstants.ExpandOtherAvatarTiming - text: qsTr("Expand /otherAvatar") - } - Action { - id: expandPaintGLTiming - objectName: "HifiAction_" + MenuConstants.ExpandPaintGLTiming - text: qsTr("Expand /paintGL") - } - Action { - id: expandUpdateTiming - objectName: "HifiAction_" + MenuConstants.ExpandUpdateTiming - text: qsTr("Expand /update") - } - Action { - id: faceshift - objectName: "HifiAction_" + MenuConstants.Faceshift - text: qsTr("Faceshift") - } - Action { - id: filterSixense - objectName: "HifiAction_" + MenuConstants.FilterSixense - text: qsTr("Smooth Sixense Movement") - } - Action { - id: firstPerson - objectName: "HifiAction_" + MenuConstants.FirstPerson - text: qsTr("First Person") - } - Action { - id: frameTimer - objectName: "HifiAction_" + MenuConstants.FrameTimer - text: qsTr("Show Timer") - } - Action { - id: fullscreen - objectName: "HifiAction_" + MenuConstants.Fullscreen - text: qsTr("Fullscreen") - } - Action { - id: fullscreenMirror - objectName: "HifiAction_" + MenuConstants.FullscreenMirror - text: qsTr("Fullscreen Mirror") - } - Action { - id: glowWhenSpeaking - objectName: "HifiAction_" + MenuConstants.GlowWhenSpeaking - text: qsTr("Glow When Speaking") - } - Action { - id: namesAboveHeads - objectName: "HifiAction_" + MenuConstants.NamesAboveHeads - text: qsTr("Names Above Heads") - } - Action { - id: goToUser - objectName: "HifiAction_" + MenuConstants.GoToUser - text: qsTr("Go To User") - } - Action { - id: hMDTools - objectName: "HifiAction_" + MenuConstants.HMDTools - text: qsTr("HMD Tools") - } - Action { - id: increaseAvatarSize - objectName: "HifiAction_" + MenuConstants.IncreaseAvatarSize - text: qsTr("Increase Avatar Size") - } - Action { - id: keyboardMotorControl - objectName: "HifiAction_" + MenuConstants.KeyboardMotorControl - text: qsTr("Enable Keyboard Motor Control") - } - Action { - id: leapMotionOnHMD - objectName: "HifiAction_" + MenuConstants.LeapMotionOnHMD - text: qsTr("Leap Motion on HMD") - } - Action { - id: loadRSSDKFile - objectName: "HifiAction_" + MenuConstants.LoadRSSDKFile - text: qsTr("Load .rssdk file") - } - Action { - id: lodTools - objectName: "HifiAction_" + MenuConstants.LodTools - text: qsTr("LOD Tools") - } - Action { - id: log - objectName: "HifiAction_" + MenuConstants.Log - text: qsTr("Log") - } - Action { - id: lowVelocityFilter - objectName: "HifiAction_" + MenuConstants.LowVelocityFilter - text: qsTr("Low Velocity Filter") - } - Action { - id: mirror - objectName: "HifiAction_" + MenuConstants.Mirror - text: qsTr("Mirror") - } - Action { - id: muteAudio - objectName: "HifiAction_" + MenuConstants.MuteAudio - text: qsTr("Mute Microphone") - } - Action { - id: muteEnvironment - objectName: "HifiAction_" + MenuConstants.MuteEnvironment - text: qsTr("Mute Environment") - } - Action { - id: noFaceTracking - objectName: "HifiAction_" + MenuConstants.NoFaceTracking - text: qsTr("None") - } - Action { - id: noShadows - objectName: "HifiAction_" + MenuConstants.NoShadows - text: qsTr("None") - } - Action { - id: octreeStats - objectName: "HifiAction_" + MenuConstants.OctreeStats - text: qsTr("Entity Statistics") - } - Action { - id: offAxisProjection - objectName: "HifiAction_" + MenuConstants.OffAxisProjection - text: qsTr("Off-Axis Projection") - } - Action { - id: onlyDisplayTopTen - objectName: "HifiAction_" + MenuConstants.OnlyDisplayTopTen - text: qsTr("Only Display Top Ten") - } - Action { - id: packageModel - objectName: "HifiAction_" + MenuConstants.PackageModel - text: qsTr("Package Model...") - } - Action { - id: pair - objectName: "HifiAction_" + MenuConstants.Pair - text: qsTr("Pair") - } - Action { - id: pipelineWarnings - objectName: "HifiAction_" + MenuConstants.PipelineWarnings - text: qsTr("Log Render Pipeline Warnings") - } - Action { - id: preferences - objectName: "HifiAction_" + MenuConstants.Preferences - text: qsTr("Preferences...") - } - Action { - id: renderBoundingCollisionShapes - objectName: "HifiAction_" + MenuConstants.RenderBoundingCollisionShapes - text: qsTr("Show Bounding Collision Shapes") - checkable: true - } - Action { - id: renderFocusIndicator - objectName: "HifiAction_" + MenuConstants.RenderFocusIndicator - text: qsTr("Show Eye Focus") - checkable: true - } - Action { - id: renderHeadCollisionShapes - objectName: "HifiAction_" + MenuConstants.RenderHeadCollisionShapes - text: qsTr("Show Head Collision Shapes") - checkable: true - } - Action { - id: renderLookAtVectors - objectName: "HifiAction_" + MenuConstants.RenderLookAtVectors - text: qsTr("Show Look-at Vectors") - checkable: true - } - Action { - id: renderSkeletonCollisionShapes - objectName: "HifiAction_" + MenuConstants.RenderSkeletonCollisionShapes - text: qsTr("Show Skeleton Collision Shapes") - checkable: true - } - ExclusiveGroup { - Action { - id: renderTargetFramerateUnlimited - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerateUnlimited - text: qsTr("Unlimited") - checked: true - checkable: true - } - Action { - id: renderTargetFramerate60 - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate60 - text: qsTr("60") - checkable: true - } - Action { - id: renderTargetFramerate50 - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate50 - text: qsTr("50") - checkable: true - } - Action { - id: renderTargetFramerate40 - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate40 - text: qsTr("40") - checkable: true - } - Action { - id: renderTargetFramerate30 - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerate30 - text: qsTr("30") - checkable: true - } - } - Action { - id: renderTargetFramerateVSyncOn - objectName: "HifiAction_" + MenuConstants.RenderTargetFramerateVSyncOn - text: qsTr("V-Sync On") - checkable: true - } - Action { - id: renderResolution - objectName: "HifiAction_" + MenuConstants.RenderResolution - text: qsTr("Scale Resolution") - } - ExclusiveGroup { - Action { - id: renderResolutionOne - objectName: "HifiAction_" + MenuConstants.RenderResolutionOne - text: qsTr("1") - checkable: true - checked: true - } - Action { - id: renderResolutionTwoThird - objectName: "HifiAction_" + MenuConstants.RenderResolutionTwoThird - text: qsTr("2/3") - checkable: true - } - Action { - id: renderResolutionHalf - objectName: "HifiAction_" + MenuConstants.RenderResolutionHalf - text: qsTr("1/2") - checkable: true - } - Action { - id: renderResolutionThird - objectName: "HifiAction_" + MenuConstants.RenderResolutionThird - text: qsTr("1/3") - checkable: true - } - Action { - id: renderResolutionQuarter - objectName: "HifiAction_" + MenuConstants.RenderResolutionQuarter - text: qsTr("1/4") - checkable: true - } - } - Action { - id: renderAmbientLight - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight - text: qsTr("Ambient Light") - } - ExclusiveGroup { - Action { - id: renderAmbientLightGlobal - objectName: "HifiAction_" + MenuConstants.RenderAmbientLightGlobal - text: qsTr("Global") - checked: true - checkable: true - } - Action { - id: renderAmbientLight0 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight0 - text: qsTr("OLD_TOWN_SQUARE") - checkable: true - } - Action { - id: renderAmbientLight1 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight1 - text: qsTr("GRACE_CATHEDRAL") - checkable: true - } - Action { - id: renderAmbientLight2 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight2 - text: qsTr("EUCALYPTUS_GROVE") - checkable: true - } - Action { - id: renderAmbientLight3 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight3 - text: qsTr("ST_PETERS_BASILICA") - checkable: true - } - Action { - id: renderAmbientLight4 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight4 - text: qsTr("UFFIZI_GALLERY") - checkable: true - } - Action { - id: renderAmbientLight5 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight5 - text: qsTr("GALILEOS_TOMB") - checkable: true - } - Action { - id: renderAmbientLight6 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight6 - text: qsTr("VINE_STREET_KITCHEN") - checkable: true - } - Action { - id: renderAmbientLight7 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight7 - text: qsTr("BREEZEWAY") - checkable: true - } - Action { - id: renderAmbientLight8 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight8 - text: qsTr("CAMPUS_SUNSET") - checkable: true - } - Action { - id: renderAmbientLight9 - objectName: "HifiAction_" + MenuConstants.RenderAmbientLight9 - text: qsTr("FUNSTON_BEACH_SUNSET") - checkable: true - } - } - Action { - id: resetAvatarSize - objectName: "HifiAction_" + MenuConstants.ResetAvatarSize - text: qsTr("Reset Avatar Size") - } - Action { - id: runTimingTests - objectName: "HifiAction_" + MenuConstants.RunTimingTests - text: qsTr("Run Timing Tests") - } - Action { - id: scriptedMotorControl - objectName: "HifiAction_" + MenuConstants.ScriptedMotorControl - text: qsTr("Enable Scripted Motor Control") - } - Action { - id: showBordersEntityNodes - objectName: "HifiAction_" + MenuConstants.ShowBordersEntityNodes - text: qsTr("Show Entity Nodes") - } - Action { - id: showIKConstraints - objectName: "HifiAction_" + MenuConstants.ShowIKConstraints - text: qsTr("Show IK Constraints") - } - Action { - id: simpleShadows - objectName: "HifiAction_" + MenuConstants.SimpleShadows - text: qsTr("Simple") - } - Action { - id: sixenseEnabled - objectName: "HifiAction_" + MenuConstants.SixenseEnabled - text: qsTr("Enable Hydra Support") - } - Action { - id: sixenseMouseInput - objectName: "HifiAction_" + MenuConstants.SixenseMouseInput - text: qsTr("Enable Sixense Mouse Input") - } - Action { - id: sixenseLasers - objectName: "HifiAction_" + MenuConstants.SixenseLasers - text: qsTr("Enable Sixense UI Lasers") - } - Action { - id: shiftHipsForIdleAnimations - objectName: "HifiAction_" + MenuConstants.ShiftHipsForIdleAnimations - text: qsTr("Shift hips for idle animations") - } - Action { - id: stars - objectName: "HifiAction_" + MenuConstants.Stars - text: qsTr("Stars") - } - Action { - id: stats - objectName: "HifiAction_" + MenuConstants.Stats - text: qsTr("Stats") - } - Action { - id: stereoAudio - objectName: "HifiAction_" + MenuConstants.StereoAudio - text: qsTr("Stereo Audio (disables spatial sound)") - } - Action { - id: suppressShortTimings - objectName: "HifiAction_" + MenuConstants.SuppressShortTimings - text: qsTr("Suppress Timings Less than 10ms") - } - Action { - id: testPing - objectName: "HifiAction_" + MenuConstants.TestPing - text: qsTr("Test Ping") - } - Action { - id: transmitterDrive - objectName: "HifiAction_" + MenuConstants.TransmitterDrive - text: qsTr("Transmitter Drive") - } - Action { - id: turnWithHead - objectName: "HifiAction_" + MenuConstants.TurnWithHead - text: qsTr("Turn using Head") - } - Action { - id: useAudioForMouth - objectName: "HifiAction_" + MenuConstants.UseAudioForMouth - text: qsTr("Use Audio for Mouth") - } - Action { - id: useCamera - objectName: "HifiAction_" + MenuConstants.UseCamera - text: qsTr("Use Camera") - } - Action { - id: velocityFilter - objectName: "HifiAction_" + MenuConstants.VelocityFilter - text: qsTr("Velocity Filter") - } - Action { - id: wireframe - objectName: "HifiAction_" + MenuConstants.Wireframe - text: qsTr("Wireframe") - } - } - - Menu { - objectName: "rootMenu"; - Menu { - title: "File" - MenuItem { - action: login - } - MenuItem { - action: aboutApp - } - MenuSeparator {} MenuItem { text: "Scripts"; enabled: false } - MenuItem { action: loadScript; } - MenuItem { action: loadScriptURL; } - MenuItem { action: stopAllScripts; } - MenuItem { action: reloadAllScripts; } - MenuItem { action: runningScripts; } - MenuSeparator {} MenuItem { text: "Locations"; enabled: false } - MenuItem { action: addressBar; } - MenuItem { action: copyAddress; } - MenuItem { action: copyPath; } - MenuSeparator {} - MenuItem { action: quit; } - } - Menu { - title: "Edit" - MenuItem { action: preferences; } - MenuItem { action: animations; } - } - Menu { - title: "Tools" - MenuItem { action: scriptEditor; } - MenuItem { action: controlWithSpeech; } - MenuItem { action: chat; } - MenuItem { action: addRemoveFriends; } - Menu { - title: "I Am Visible To" - MenuItem { action: visibleToEveryone; } - MenuItem { action: visibleToFriends; } - MenuItem { action: visibleToNoOne; } - } - MenuItem { action: toolWindow; } - MenuItem { action: javascriptConsole; } - MenuItem { action: resetSensors; } - MenuItem { action: packageModel; } - } - Menu { - title: "Avatar" - Menu { - title: "Size" - MenuItem { action: increaseAvatarSize; } - MenuItem { action: decreaseAvatarSize; } - MenuItem { action: resetAvatarSize; } - } - MenuItem { action: keyboardMotorControl; } - MenuItem { action: scriptedMotorControl; } - MenuItem { action: namesAboveHeads; } - MenuItem { action: glowWhenSpeaking; } - MenuItem { action: blueSpeechSphere; } - MenuItem { action: enableCharacterController; } - MenuItem { action: shiftHipsForIdleAnimations; } - } - Menu { - title: "View" - MenuItem { action: fullscreen; } - MenuItem { action: firstPerson; } - MenuItem { action: mirror; } - MenuItem { action: fullscreenMirror; } - MenuItem { action: hMDTools; } - MenuItem { action: enableVRMode; } - MenuItem { action: enable3DTVMode; } - MenuItem { action: showBordersEntityNodes; } - MenuItem { action: offAxisProjection; } - MenuItem { action: turnWithHead; } - MenuItem { action: stats; } - MenuItem { action: log; } - MenuItem { action: bandwidthDetails; } - MenuItem { action: octreeStats; } - } - Menu { - title: "Developer" - Menu { - title: "Render" - MenuItem { action: atmosphere; } - MenuItem { action: ambientOcclusion; } - MenuItem { action: dontFadeOnOctreeServerChanges; } - Menu { - title: "Ambient Light" - MenuItem { action: renderAmbientLightGlobal; } - MenuItem { action: renderAmbientLight0; } - MenuItem { action: renderAmbientLight1; } - MenuItem { action: renderAmbientLight2; } - MenuItem { action: renderAmbientLight3; } - MenuItem { action: renderAmbientLight4; } - MenuItem { action: renderAmbientLight5; } - MenuItem { action: renderAmbientLight6; } - MenuItem { action: renderAmbientLight7; } - MenuItem { action: renderAmbientLight8; } - MenuItem { action: renderAmbientLight9; } - } - Menu { - title: "Shadows" - MenuItem { action: noShadows; } - MenuItem { action: simpleShadows; } - MenuItem { action: cascadedShadows; } - } - Menu { - title: "Framerate" - MenuItem { action: renderTargetFramerateUnlimited; } - MenuItem { action: renderTargetFramerate60; } - MenuItem { action: renderTargetFramerate50; } - MenuItem { action: renderTargetFramerate40; } - MenuItem { action: renderTargetFramerate30; } - } - MenuItem { action: renderTargetFramerateVSyncOn; } - Menu { - title: "Scale Resolution" - MenuItem { action: renderResolutionOne; } - MenuItem { action: renderResolutionTwoThird; } - MenuItem { action: renderResolutionHalf; } - MenuItem { action: renderResolutionThird; } - MenuItem { action: renderResolutionQuarter; } - } - MenuItem { action: stars; } - MenuItem { action: enableGlowEffect; } - MenuItem { action: wireframe; } - MenuItem { action: lodTools; } - } - Menu { - title: "Avatar" - Menu { - title: "Face Tracking" - MenuItem { action: noFaceTracking; } - MenuItem { action: faceshift; } - MenuItem { action: useCamera; } - MenuItem { action: useAudioForMouth; } - MenuItem { action: velocityFilter; } - } - MenuItem { action: renderSkeletonCollisionShapes; } - MenuItem { action: renderHeadCollisionShapes; } - MenuItem { action: renderBoundingCollisionShapes; } - MenuItem { action: renderLookAtVectors; } - MenuItem { action: renderFocusIndicator; } - } - Menu { - title: "Hands" - MenuItem { action: alignForearmsWithWrists; } - MenuItem { action: alternateIK; } - MenuItem { action: displayHands; } - MenuItem { action: displayHandTargets; } - MenuItem { action: showIKConstraints; } - Menu { - title: "Sixense" - MenuItem { action: sixenseEnabled; } - MenuItem { action: filterSixense; } - MenuItem { action: lowVelocityFilter; } - MenuItem { action: sixenseMouseInput; } - MenuItem { action: sixenseLasers; } - } - Menu { - title: "Leap Motion" - MenuItem { action: leapMotionOnHMD; } - } - MenuItem { action: loadRSSDKFile; } - } - Menu { - title: "Network" - MenuItem { action: disableNackPackets; } - MenuItem { action: disableActivityLogger; } - MenuItem { action: cachesSize; } - MenuItem { action: diskCacheEditor; } - } - Menu { - title: "Timing and Stats" - Menu { - title: "Performance Timer" - MenuItem { action: displayDebugTimingDetails; } - MenuItem { action: onlyDisplayTopTen; } - MenuItem { action: expandUpdateTiming; } - MenuItem { action: expandMyAvatarTiming; } - MenuItem { action: expandMyAvatarSimulateTiming; } - MenuItem { action: expandOtherAvatarTiming; } - MenuItem { action: expandPaintGLTiming; } - } - MenuItem { action: testPing; } - MenuItem { action: frameTimer; } - MenuItem { action: runTimingTests; } - MenuItem { action: pipelineWarnings; } - MenuItem { action: suppressShortTimings; } - } - Menu { - title: "Audio" - MenuItem { action: audioNoiseReduction; } - MenuItem { action: echoServerAudio; } - MenuItem { action: echoLocalAudio; } - MenuItem { action: stereoAudio; } - MenuItem { action: muteAudio; } - MenuItem { action: muteEnvironment; } - Menu { - title: "Audio Scope" - MenuItem { action: audioScope; } - MenuItem { action: audioScopePause; } - MenuSeparator {} MenuItem { text: "Audio Scope"; enabled: false; } - MenuItem { action: audioScopeFiveFrames; } - MenuItem { action: audioScopeTwentyFrames; } - MenuItem { action: audioScopeFiftyFrames; } - } - MenuItem { action: audioStats; } - MenuItem { action: audioStatsShowInjectedStreams; } - } - } - Menu { - title: "Help" - MenuItem { action: editEntitiesHelp; } - MenuItem { action: aboutApp; } - } - } -} diff --git a/interface/resources/qml/MessageDialog.qml b/interface/resources/qml/MessageDialog.qml index 02f1e4716f..dd410b8070 100644 --- a/interface/resources/qml/MessageDialog.qml +++ b/interface/resources/qml/MessageDialog.qml @@ -325,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/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/VrMenu.qml b/interface/resources/qml/VrMenu.qml index 01a726d2ba..d81d79aa0b 100644 --- a/interface/resources/qml/VrMenu.qml +++ b/interface/resources/qml/VrMenu.qml @@ -128,13 +128,16 @@ Hifi.VrMenu { // visible: source.visible Row { id: row - spacing: 4 + 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 @@ -191,6 +194,7 @@ Hifi.VrMenu { bottom: parent.bottom left: parent.left right: tag.right + rightMargin: -4 } acceptedButtons: Qt.LeftButton hoverEnabled: true @@ -263,23 +267,22 @@ Hifi.VrMenu { 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: @@ -317,7 +320,7 @@ Hifi.VrMenu { root.popColumn(); } else { root.enabled = false; - } + } } } } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 8ba7b514e4..d9c7cd890d 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -426,7 +426,6 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::functionfindChild(name); } if (item) { - qDebug() << "Turning item " << !item->isEnabled(); item->setEnabled(!item->isEnabled()); } } diff --git a/tests/ui/src/main.cpp b/tests/ui/src/main.cpp index aaca2efa2a..a5bc50b288 100644 --- a/tests/ui/src/main.cpp +++ b/tests/ui/src/main.cpp @@ -348,7 +348,7 @@ public: #else offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->load(QUrl("TestRoot.qml")); - offscreenUi->load(QUrl("InterfaceMenu.qml")); + offscreenUi->load(QUrl("TestMenu.qml")); // Requires a root menu to have been loaded before it can load VrMenu::load(); #endif @@ -470,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"; @@ -484,7 +484,7 @@ qt.quick.mouse.debug=false 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; From b0eccc40c27b0cb8a87af5186393ca663058f960 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 27 Apr 2015 22:58:09 -0700 Subject: [PATCH 25/33] Fix crash cause when Menu::instance() is before offscreen ui startup --- interface/src/Application.cpp | 3 +++ interface/src/Menu.cpp | 20 +++++++++++++++----- libraries/ui/src/VrMenu.cpp | 26 +++++++++++++++++++------- libraries/ui/src/VrMenu.h | 3 ++- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f1825b2c4a..3b2173f05e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -767,6 +767,7 @@ void Application::initializeUi() { AddressBarDialog::registerType(); LoginDialog::registerType(); MessageDialog::registerType(); + VrMenu::registerType(); auto offscreenUi = DependencyManager::get(); offscreenUi->create(_glWidget->context()->contextHandle()); @@ -775,6 +776,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)); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index c467658fc4..868fa9270c 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -975,7 +975,9 @@ bool Menu::menuItemExists(const QString& menu, const QString& menuitem) { MenuWrapper::MenuWrapper(QMenu* menu) : _realMenu(menu) { - VrMenu::instance()->addMenu(menu); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addMenu(menu); + }); _backMap[menu] = this; } @@ -997,18 +999,24 @@ void MenuWrapper::addSeparator() { void MenuWrapper::addAction(QAction* action) { _realMenu->addAction(action); - VrMenu::instance()->addAction(_realMenu, action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); } QAction* MenuWrapper::addAction(const QString& menuName) { QAction* action = _realMenu->addAction(menuName); - VrMenu::instance()->addAction(_realMenu, action); + 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::instance()->addAction(_realMenu, action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->addAction(_realMenu, action); + }); return action; } @@ -1018,7 +1026,9 @@ void MenuWrapper::removeAction(QAction* action) { void MenuWrapper::insertAction(QAction* before, QAction* action) { _realMenu->insertAction(before, action); - VrMenu::instance()->insertAction(before, action); + VrMenu::executeOrQueue([=](VrMenu* vrMenu) { + vrMenu->insertAction(before, action); + }); } QHash MenuWrapper::_backMap; diff --git a/libraries/ui/src/VrMenu.cpp b/libraries/ui/src/VrMenu.cpp index 04c2cb0b30..5c0f8fb732 100644 --- a/libraries/ui/src/VrMenu.cpp +++ b/libraries/ui/src/VrMenu.cpp @@ -42,7 +42,7 @@ private: 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())); + Q_ASSERT(VrMenu::_instance->findMenuObject(uuid.toString())); } }; @@ -57,14 +57,25 @@ HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) { }); VrMenu* VrMenu::_instance{ nullptr }; +static QQueue> queuedLambdas; -VrMenu* VrMenu::instance() { - if (!_instance) { - VrMenu::registerType(); - VrMenu::load(); - Q_ASSERT(_instance); +void VrMenu::executeOrQueue(std::function f) { + if (_instance) { + foreach(std::function priorLambda, queuedLambdas) { + priorLambda(_instance); + } + f(_instance); + } else { + queuedLambdas.push_back(f); } - return _instance; +} + +void VrMenu::executeQueuedLambdas() { + Q_ASSERT(_instance); + foreach(std::function f, queuedLambdas) { + f(_instance); + } + queuedLambdas.clear(); } VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { @@ -72,6 +83,7 @@ VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) { this->setEnabled(false); } + // QML helper functions QObject* addMenu(QObject* parent, const QString& text) { // FIXME add more checking here to ensure no name conflicts diff --git a/libraries/ui/src/VrMenu.h b/libraries/ui/src/VrMenu.h index 7fe0d33cc4..06a16588af 100644 --- a/libraries/ui/src/VrMenu.h +++ b/libraries/ui/src/VrMenu.h @@ -26,7 +26,8 @@ class VrMenu : public QQuickItem { HIFI_QML_DECL_LAMBDA public: - static VrMenu* instance(); + static void executeOrQueue(std::function f); + static void executeQueuedLambdas(); VrMenu(QQuickItem* parent = nullptr); void addMenu(QMenu* menu); void addAction(QMenu* parent, QAction* action); From d1d1830b2f811ae181b7055a82bab7878ef85afb Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 28 Apr 2015 00:26:24 -0700 Subject: [PATCH 26/33] Importing peferred dialog look from browser branch --- interface/resources/qml/controls/Dialog.qml | 12 +++++++ .../resources/qml/controls/DialogBase.qml | 32 ++++++++++++++++--- .../resources/qml/controls/TextInput.qml | 4 +-- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/interface/resources/qml/controls/Dialog.qml b/interface/resources/qml/controls/Dialog.qml index 629813d3b5..46add1dc07 100644 --- a/interface/resources/qml/controls/Dialog.qml +++ b/interface/resources/qml/controls/Dialog.qml @@ -92,6 +92,7 @@ DialogBase { anchors.bottom: parent.bottom width: 16 height: 16 + z: 1000 hoverEnabled: true onPressed: { startX = mouseX @@ -143,4 +144,15 @@ DialogBase { } } } + + 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 index 050237c184..a6616fc731 100644 --- a/interface/resources/qml/controls/DialogBase.qml +++ b/interface/resources/qml/controls/DialogBase.qml @@ -22,11 +22,13 @@ Item { 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 + hifi.styles.borderWidth - readonly property real clientY: clientBorder.y + hifi.styles.borderWidth - readonly property real clientWidth: clientBorder.width - hifi.styles.borderWidth * 2 - readonly property real clientHeight: clientBorder.height - hifi.styles.borderWidth * 2 + 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 @@ -35,6 +37,7 @@ Item { id: windowBorder anchors.fill: parent border.color: root.frameColor + border.width: 0 color: "#00000000" Border { @@ -43,11 +46,13 @@ Item { 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 ? @@ -60,8 +65,26 @@ Item { } } // 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 @@ -69,7 +92,6 @@ Item { anchors.topMargin: -titleBorder.border.width anchors.right: parent.right anchors.left: parent.left - clip: true } // client border } // window border diff --git a/interface/resources/qml/controls/TextInput.qml b/interface/resources/qml/controls/TextInput.qml index ceaca0c073..d533c67bd6 100644 --- a/interface/resources/qml/controls/TextInput.qml +++ b/interface/resources/qml/controls/TextInput.qml @@ -13,14 +13,14 @@ Original.TextInput { font.family: hifi.fonts.fontFamily font.pointSize: hifi.fonts.fontSize - +/* Original.Rectangle { // Render the rectangle as background z: -1 anchors.fill: parent color: hifi.colors.inputBackground } - +*/ Text { anchors.fill: parent font.pointSize: parent.font.pointSize From 25753524852679e317fe790282c635196eb33309 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 28 Apr 2015 12:51:01 -0700 Subject: [PATCH 27/33] Attempting to resolve possible crash case for scripts playing sounds --- libraries/script-engine/src/ScriptAudioInjector.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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); From 512a10902348f7bcbe58e49416a5a84d980d75e9 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Apr 2015 13:48:51 -0700 Subject: [PATCH 28/33] add call to perror() to get OS level error details on failed backups --- libraries/octree/src/OctreePersistThread.cpp | 3 +++ 1 file changed, 3 insertions(+) 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. " << From 9576ad19a7f78001ec5f90df899190f6d931d128 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 28 Apr 2015 10:52:46 -0700 Subject: [PATCH 29/33] Move "Stereo Audio" option into developerMenu.js And rename it "Stereo Input" to better relect what it does. --- examples/utilities/tools/developerMenuItems.js | 12 ++++++++++++ interface/src/Menu.cpp | 2 -- interface/src/Menu.h | 1 - libraries/audio-client/src/AudioClient.h | 8 +++----- libraries/audio/src/AbstractAudioInterface.h | 2 ++ .../script-engine/src/AudioScriptingInterface.cpp | 6 +++++- .../script-engine/src/AudioScriptingInterface.h | 2 ++ 7 files changed, 24 insertions(+), 9 deletions(-) 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/src/Menu.cpp b/interface/src/Menu.cpp index 43c63eb383..4548ccd586 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -485,8 +485,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, diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 2c4e2b809f..fd0c230c17 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -238,7 +238,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/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/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(); From 342d3576c96b62d98fbd2f436c49836ca0accc74 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 28 Apr 2015 14:46:39 -0700 Subject: [PATCH 30/33] Added ability to check if an avatar is within specified range of a position from javascript --- libraries/avatars/src/AvatarHashMap.cpp | 11 +++++++++++ libraries/avatars/src/AvatarHashMap.h | 2 ++ 2 files changed, 13 insertions(+) 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: From bfbad539e06a3881cf850cd9bcc4c7c0a0eeb786 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Apr 2015 14:54:58 -0700 Subject: [PATCH 31/33] hack test --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index cd7bedc19e..e588cb69f1 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -166,7 +166,7 @@ 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); + //Application::getInstance()->getCamera()->setScale(scale); } { From aec0bd16930adcb82d22beb6aa48a2bb9c98fe0e Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Apr 2015 16:26:17 -0700 Subject: [PATCH 32/33] dont change camera scale with avatar scale --- interface/src/avatar/MyAvatar.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index e588cb69f1..60a760b2a8 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); } { From 664d1264653261f6ccee8049c50f1160eaf95d72 Mon Sep 17 00:00:00 2001 From: ZappoMan Date: Tue, 28 Apr 2015 16:34:33 -0700 Subject: [PATCH 33/33] remove scale from Camera class since it was only used by avatar and was causing problems with no obvious benefit --- interface/src/Camera.cpp | 5 ++--- interface/src/Camera.h | 5 +---- interface/src/avatar/MyAvatar.cpp | 2 -- 3 files changed, 3 insertions(+), 9 deletions(-) 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/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 60a760b2a8..557d630ebf 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -672,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