From 8cb298a55b9ed36ad161c21d50021aa97cc22707 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 22 Apr 2015 18:11:42 -0700 Subject: [PATCH] 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"