Switching to a wrapper mechanism for VR menus

This commit is contained in:
Brad Davis 2015-04-25 16:20:15 -07:00
parent ee30588fd4
commit a96f69a673
30 changed files with 1335 additions and 1045 deletions

View file

@ -48,7 +48,7 @@ Dialog {
helperText: "domain, location, @user, /x,y,z" helperText: "domain, location, @user, /x,y,z"
anchors.margins: 8 anchors.margins: 8
onAccepted: { onAccepted: {
event.accepted event.accepted
addressBarDialog.loadAddress(addressLine.text) addressBarDialog.loadAddress(addressLine.text)
} }
} }

View file

@ -2,6 +2,7 @@ import QtQuick 2.4
import QtQuick.Controls 1.3 import QtQuick.Controls 1.3
import Hifi 1.0 import Hifi 1.0
// Currently for testing a pure QML replacement menu
Item { Item {
Item { Item {
objectName: "AllActions" objectName: "AllActions"
@ -10,7 +11,7 @@ Item {
objectName: "HifiAction_" + MenuConstants.AboutApp objectName: "HifiAction_" + MenuConstants.AboutApp
text: qsTr("About Interface") text: qsTr("About Interface")
} }
// //
// File Menu // File Menu
// //
@ -26,7 +27,7 @@ Item {
//shortcut: StandardKey.Quit //shortcut: StandardKey.Quit
shortcut: "Ctrl+Q" shortcut: "Ctrl+Q"
} }
// Scripts // Scripts
Action { Action {
id: loadScript id: loadScript
@ -58,7 +59,7 @@ Item {
objectName: "HifiAction_" + MenuConstants.StopAllScripts objectName: "HifiAction_" + MenuConstants.StopAllScripts
text: qsTr("Stop All Scripts") text: qsTr("Stop All Scripts")
} }
// Locations // Locations
Action { Action {
id: bookmarkLocation id: bookmarkLocation
@ -75,7 +76,7 @@ Item {
objectName: "HifiAction_" + MenuConstants.AddressBar objectName: "HifiAction_" + MenuConstants.AddressBar
text: qsTr("Show Address Bar") text: qsTr("Show Address Bar")
} }
// //
// Edit menu // Edit menu
// //
@ -84,13 +85,13 @@ Item {
text: "Undo" text: "Undo"
shortcut: StandardKey.Undo shortcut: StandardKey.Undo
} }
Action { Action {
id: redo id: redo
text: "Redo" text: "Redo"
shortcut: StandardKey.Redo shortcut: StandardKey.Redo
} }
Action { Action {
id: animations id: animations
objectName: "HifiAction_" + MenuConstants.Animations objectName: "HifiAction_" + MenuConstants.Animations
@ -157,7 +158,7 @@ Item {
objectName: "HifiAction_" + MenuConstants.ResetSensors objectName: "HifiAction_" + MenuConstants.ResetSensors
text: qsTr("Reset Sensors") text: qsTr("Reset Sensors")
} }
@ -853,7 +854,7 @@ Item {
} }
} }
MenuBar { Menu {
objectName: "rootMenu"; objectName: "rootMenu";
Menu { Menu {
title: "File" title: "File"

View file

@ -1,20 +0,0 @@
import QtQuick 2.4
import QtQuick.Controls 1.3
ApplicationWindow {
id: root
width: 800
height: 600
visible: true
menuBar: MenuBar {
Menu {
title: "File"
MenuItem {
text: "Quit"
}
}
}
}

View file

@ -9,6 +9,7 @@ Dialog {
property real spacing: 8 property real spacing: 8
property real outerSpacing: 16 property real outerSpacing: 16
destroyOnCloseButton: true destroyOnCloseButton: true
destroyOnInvisible: true destroyOnInvisible: true
implicitHeight: content.implicitHeight + outerSpacing * 2 + 48 implicitHeight: content.implicitHeight + outerSpacing * 2 + 48
@ -35,7 +36,7 @@ Dialog {
onEnabledChanged: { onEnabledChanged: {
if (enabled) { if (enabled) {
root.forceActiveFocus(); root.forceActiveFocus();
} }
} }
@ -334,7 +335,7 @@ Dialog {
case Qt.Key_Period: case Qt.Key_Period:
if (Qt.platform.os === "osx") { if (Qt.platform.os === "osx") {
event.accepted = true event.accepted = true
content.reject() content.reject()
} }
break break
} else switch (event.key) { } else switch (event.key) {
@ -346,7 +347,7 @@ Dialog {
case Qt.Key_Enter: case Qt.Key_Enter:
case Qt.Key_Return: case Qt.Key_Return:
console.log("Accepting"); console.log("Accepting");
event.accepted = true event.accepted = true
content.accept() content.accept()
break break

View file

@ -8,7 +8,7 @@ Root {
anchors.fill: parent anchors.fill: parent
onParentChanged: { onParentChanged: {
forceActiveFocus(); forceActiveFocus();
} }
} }

View file

@ -6,12 +6,11 @@ import QtQuick.Controls 1.3
import "controls" import "controls"
Root { Root {
id: root id: root
anchors.fill: parent anchors.fill: parent
onParentChanged: { onParentChanged: {
forceActiveFocus(); forceActiveFocus();
} }
Button { Button {
id: messageBox id: messageBox
anchors.right: createDialog.left anchors.right: createDialog.left
@ -38,7 +37,7 @@ Root {
} }
Keys.onPressed: { Keys.onPressed: {
console.log(event.key); console.log("Key press root")
} }
} }

View file

@ -5,10 +5,10 @@ import QtQuick.Controls.Styles 1.3
import "controls" import "controls"
import "styles" import "styles"
Hifi.HifiMenu { Hifi.VrMenu {
id: root id: root
anchors.fill: parent anchors.fill: parent
objectName: "HifiMenu" objectName: "VrMenu"
enabled: false enabled: false
opacity: 0.0 opacity: 0.0
property int animationDuration: 200 property int animationDuration: 200
@ -17,7 +17,7 @@ Hifi.HifiMenu {
onEnabledChanged: { onEnabledChanged: {
if (enabled && columns.length == 0) { if (enabled && columns.length == 0) {
pushColumn(rootMenu.menus); pushColumn(rootMenu.items);
} }
opacity = enabled ? 1.0 : 0.0 opacity = enabled ? 1.0 : 0.0
if (enabled) { if (enabled) {
@ -47,13 +47,21 @@ Hifi.HifiMenu {
property var menuBuilder: Component { property var menuBuilder: Component {
Border { Border {
Component.onCompleted: {
menuDepth = root.models.length - 1
if (menuDepth == 0) {
x = lastMousePosition.x - 20
y = lastMousePosition.y - 20
} else {
var lastColumn = root.columns[menuDepth - 1]
x = lastColumn.x + 64;
y = lastMousePosition.y - height / 2;
}
}
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
x: root.models.length == 1 ?
(root.width / 2 - width / 2) :
root.columns[root.models.length - 2].x + 60;
anchors.verticalCenter: parent.verticalCenter
border.color: hifiPalette.hifiBlue border.color: hifiPalette.hifiBlue
color: sysPalette.window color: sysPalette.window
property int menuDepth
ListView { ListView {
spacing: 6 spacing: 6
@ -186,7 +194,25 @@ Hifi.HifiMenu {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 0 anchors.topMargin: 0
width: listView.width width: listView.width
hoverEnabled: true
Timer {
id: timer
interval: 1000
onTriggered: parent.select();
}
onEntered: {
if (source.type == 2 && enabled) {
timer.start()
}
}
onExited: {
timer.stop()
}
onClicked: { onClicked: {
select();
}
function select() {
timer.stop();
listView.currentIndex = listViewIndex listView.currentIndex = listViewIndex
parent.root.selectItem(parent.source); parent.root.selectItem(parent.source);
} }

View file

@ -17,7 +17,7 @@ Item {
HifiPalette { id: hifiPalette } HifiPalette { id: hifiPalette }
SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active } SystemPalette { id: sysPalette; colorGroup: SystemPalette.Active }
x: parent ? parent.width / 2 - width / 2 : 0 x: parent ? parent.width / 2 - width / 2 : 0
y: parent ? parent.height / 2 - height / 2 : 0 y: parent ? parent.height / 2 - height / 2 : 0
property int animationDuration: 400 property int animationDuration: 400
property bool destroyOnInvisible: false property bool destroyOnInvisible: false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 B

View file

@ -52,8 +52,6 @@
#include <QMimeData> #include <QMimeData>
#include <QMessageBox> #include <QMessageBox>
#include <QJsonDocument> #include <QJsonDocument>
#include <QQmlNetworkAccessManagerFactory>
#include <QThreadStorage>
#include <AddressManager.h> #include <AddressManager.h>
#include <AccountManager.h> #include <AccountManager.h>
@ -65,6 +63,7 @@
#include <GlowEffect.h> #include <GlowEffect.h>
#include <HFActionEvent.h> #include <HFActionEvent.h>
#include <HFBackEvent.h> #include <HFBackEvent.h>
#include <HifiMenu.h>
#include <LogHandler.h> #include <LogHandler.h>
#include <MainWindow.h> #include <MainWindow.h>
#include <ModelEntityItem.h> #include <ModelEntityItem.h>
@ -135,7 +134,6 @@
#include "ui/DialogsManager.h" #include "ui/DialogsManager.h"
#include "ui/InfoView.h" #include "ui/InfoView.h"
#include "ui/LoginDialog.h" #include "ui/LoginDialog.h"
#include "ui/MarketplaceDialog.h"
#include "ui/Snapshot.h" #include "ui/Snapshot.h"
#include "ui/StandAloneJSConsole.h" #include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h" #include "ui/Stats.h"
@ -510,6 +508,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// enable mouse tracking; otherwise, we only get drag events // enable mouse tracking; otherwise, we only get drag events
_glWidget->setMouseTracking(true); _glWidget->setMouseTracking(true);
_fullscreenMenuWidget->setParent(_glWidget);
_menuBarHeight = Menu::getInstance()->height();
if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { if (Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) {
setFullscreen(true); // Initialize menu bar show/hide setFullscreen(true); // Initialize menu bar show/hide
} }
@ -730,6 +730,10 @@ void Application::initializeGL() {
qCDebug(interfaceapp, "Initialized Offscreen UI."); qCDebug(interfaceapp, "Initialized Offscreen UI.");
_glWidget->makeCurrent(); _glWidget->makeCurrent();
// call Menu getInstance static method to set up the menu
// Needs to happen AFTER the QML UI initialization
_window->setMenuBar(Menu::getInstance());
init(); init();
qCDebug(interfaceapp, "init() complete."); qCDebug(interfaceapp, "init() complete.");
@ -764,9 +768,7 @@ void Application::initializeGL() {
void Application::initializeUi() { void Application::initializeUi() {
AddressBarDialog::registerType(); AddressBarDialog::registerType();
LoginDialog::registerType(); LoginDialog::registerType();
MarketplaceDialog::registerType();
MessageDialog::registerType(); MessageDialog::registerType();
Menu::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle()); offscreenUi->create(_glWidget->context()->contextHandle());
@ -902,7 +904,9 @@ void Application::runTests() {
} }
void Application::audioMuteToggled() { void Application::audioMuteToggled() {
Menu::getInstance()->setIsOptionChecked(MenuOption::MuteAudio, DependencyManager::get<AudioClient>()->isMuted()); QAction* muteAction = Menu::getInstance()->getActionForOption(MenuOption::MuteAudio);
Q_CHECK_PTR(muteAction);
muteAction->setChecked(DependencyManager::get<AudioClient>()->isMuted());
} }
void Application::aboutApp() { void Application::aboutApp() {
@ -1067,12 +1071,10 @@ bool Application::eventFilter(QObject* object, QEvent* event) {
return false; return false;
} }
static bool altPressed; static bool _altPressed{ false };
static bool ctrlPressed;
void Application::keyPressEvent(QKeyEvent* event) { void Application::keyPressEvent(QKeyEvent* event) {
altPressed = event->key() == Qt::Key_Alt; _altPressed = event->key() == Qt::Key_Alt;
ctrlPressed = event->key() == Qt::Key_Control;
_keysPressed.insert(event->key()); _keysPressed.insert(event->key());
_controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts _controllerScriptingInterface.emitKeyPressEvent(event); // send events to any registered scripts
@ -1162,9 +1164,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
break; break;
case Qt::Key_Backslash: case Qt::Key_Backslash:
MarketplaceDialog::show(); Menu::getInstance()->triggerOption(MenuOption::Chat);
//Menu::getInstance()->triggerOption(MenuOption::Chat);
break; break;
case Qt::Key_Up: case Qt::Key_Up:
@ -1329,13 +1329,15 @@ void Application::keyPressEvent(QKeyEvent* event) {
} }
void Application::keyReleaseEvent(QKeyEvent* event) { void Application::keyReleaseEvent(QKeyEvent* event) {
if (event->key() == Qt::Key_Alt && altPressed && _window->isActiveWindow()) { if (event->key() == Qt::Key_Alt && _altPressed && _window->isActiveWindow()) {
Menu::toggle(); #ifndef DEBUG
if (OculusManager::isConnected()) {
#endif
VrMenu::toggle();
#ifndef DEBUG
}
#endif
} }
if (event->key() == Qt::Key_Control && ctrlPressed && _window->isActiveWindow()) {
Menu::toggle();
}
ctrlPressed = altPressed = false;
_keysPressed.remove(event->key()); _keysPressed.remove(event->key());
@ -1346,6 +1348,7 @@ void Application::keyReleaseEvent(QKeyEvent* event) {
return; return;
} }
switch (event->key()) { switch (event->key()) {
case Qt::Key_E: case Qt::Key_E:
case Qt::Key_PageUp: case Qt::Key_PageUp:
@ -1442,6 +1445,18 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
return; 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); _entities.mouseMoveEvent(event, deviceID);
_controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts _controllerScriptingInterface.emitMouseMoveEvent(event, deviceID); // send events to any registered scripts
@ -1449,6 +1464,7 @@ void Application::mouseMoveEvent(QMouseEvent* event, unsigned int deviceID) {
if (_controllerScriptingInterface.isMouseCaptured()) { if (_controllerScriptingInterface.isMouseCaptured()) {
return; return;
} }
} }
void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) { void Application::mousePressEvent(QMouseEvent* event, unsigned int deviceID) {
@ -1735,6 +1751,34 @@ void Application::setFullscreen(bool fullscreen) {
Menu::getInstance()->getActionForOption(MenuOption::Fullscreen)->setChecked(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. // Work around Qt bug that prevents floating menus being shown when in fullscreen mode.
// https://bugreports.qt.io/browse/QTBUG-41883 // 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. // Known issue: Top-level menu items don't highlight when cursor hovers. This is probably a side-effect of the work-around.
@ -1987,18 +2031,16 @@ void Application::init() {
OculusManager::connect(); OculusManager::connect();
if (OculusManager::isConnected()) { if (OculusManager::isConnected()) {
// perform as a post-event so that the code is run after init is complete QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
qApp->postLambdaEvent([] { "trigger",
Menu::getInstance()->triggerOption(MenuOption::Fullscreen); Qt::QueuedConnection);
});
} }
TV3DManager::connect(); TV3DManager::connect();
if (TV3DManager::isConnected()) { if (TV3DManager::isConnected()) {
// perform as a post-event so that the code is run after init is complete QMetaObject::invokeMethod(Menu::getInstance()->getActionForOption(MenuOption::Fullscreen),
qApp->postLambdaEvent([] { "trigger",
Menu::getInstance()->triggerOption(MenuOption::Fullscreen); Qt::QueuedConnection);
});
} }
_timerStart.start(); _timerStart.start();

View file

@ -623,6 +623,9 @@ private:
void checkSkeleton(); void checkSkeleton();
QWidget* _fullscreenMenuWidget = new QWidget();
int _menuBarHeight;
QHash<QString, AcceptURLMethod> _acceptedExtensions; QHash<QString, AcceptURLMethod> _acceptedExtensions;
QList<QString> _domainConnectionRefusals; QList<QString> _domainConnectionRefusals;

View file

@ -84,16 +84,13 @@ void Bookmarks::persistToFile() {
saveFile.write(data); saveFile.write(data);
} }
void Bookmarks::setupMenus(const QString & parentMenu) { void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) {
// Add menus/actions // Add menus/actions
Menu * menu = Menu::getInstance(); menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation, 0,
menu->addItem(parentMenu, MenuOption::BookmarkLocation, [this] { this, SLOT(bookmarkLocation()));
bookmarkLocation(); _bookmarksMenu = menu->addMenu(MenuOption::Bookmarks);
}); _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark, 0,
menu->addMenu(parentMenu, MenuOption::Bookmarks); this, SLOT(deleteBookmark()));
menu->addItem(parentMenu, MenuOption::DeleteBookmark, [this] {
deleteBookmark();
});
// Enable/Disable menus as needed // Enable/Disable menus as needed
enableMenuItems(_bookmarks.count() > 0); enableMenuItems(_bookmarks.count() > 0);
@ -102,7 +99,7 @@ void Bookmarks::setupMenus(const QString & parentMenu) {
for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) { for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) {
QString bookmarkName = it.key(); QString bookmarkName = it.key();
QString bookmarkAddress = it.value().toString(); QString bookmarkAddress = it.value().toString();
addLocationToMenu(bookmarkName, bookmarkAddress); addLocationToMenu(menubar, bookmarkName, bookmarkAddress);
} }
} }
@ -137,20 +134,29 @@ void Bookmarks::bookmarkLocation() {
if (duplicateBookmarkMessage.exec() == QMessageBox::No) { if (duplicateBookmarkMessage.exec() == QMessageBox::No) {
return; return;
} }
removeLocationFromMenu(bookmarkName); removeLocationFromMenu(menubar, bookmarkName);
} }
addLocationToMenu(bookmarkName, bookmarkAddress); addLocationToMenu(menubar, bookmarkName, bookmarkAddress);
insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName.
enableMenuItems(true); enableMenuItems(true);
} }
void Bookmarks::teleportToBookmark() {
QAction* action = qobject_cast<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Bookmarks::deleteBookmark() { void Bookmarks::deleteBookmark() {
QStringList bookmarkList; QStringList bookmarkList;
bookmarkList.append(_bookmarks.keys()); QList<QAction*> menuItems = _bookmarksMenu->actions();
for (int i = 0; i < menuItems.count(); i += 1) {
bookmarkList.append(menuItems[i]->text());
}
QInputDialog deleteBookmarkDialog(qApp->getWindow()); QInputDialog deleteBookmarkDialog(qApp->getWindow());
deleteBookmarkDialog.setWindowTitle("Delete Bookmark"); deleteBookmarkDialog.setWindowTitle("Delete Bookmark");
deleteBookmarkDialog.setLabelText("Select the bookmark to delete"); deleteBookmarkDialog.setLabelText("Select the bookmark to delete");
@ -168,29 +174,34 @@ void Bookmarks::deleteBookmark() {
return; return;
} }
removeLocationFromMenu(bookmarkName); removeLocationFromMenu(Menu::getInstance(), bookmarkName);
remove(bookmarkName); remove(bookmarkName);
if (_bookmarks.keys().isEmpty()) { if (_bookmarksMenu->actions().count() == 0) {
enableMenuItems(false); enableMenuItems(false);
} }
} }
void Bookmarks::enableMenuItems(bool enabled) { void Bookmarks::enableMenuItems(bool enabled) {
Menu* menu = Menu::getInstance(); if (_bookmarksMenu) {
menu->enableItem(MenuOption::Bookmarks, enabled); _bookmarksMenu->setEnabled(enabled);
menu->enableItem(MenuOption::DeleteBookmark, enabled); }
if (_deleteBookmarksAction) {
_deleteBookmarksAction->setEnabled(enabled);
}
} }
void Bookmarks::addLocationToMenu(QString& name, QString& address) { void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) {
QAction* teleportAction = _bookmarksMenu->newAction();
Menu::getInstance()->addItem(MenuOption::Bookmarks, name, [=] { teleportAction->setData(address);
DependencyManager::get<AddressManager>()->handleLookupString(address); connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark()));
});
menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction,
name, 0, QAction::NoRole);
} }
void Bookmarks::removeLocationFromMenu(QString& name) { void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) {
Menu::getInstance()->removeItem(name); menubar->removeAction(_bookmarksMenu, name);
} }

View file

@ -19,6 +19,7 @@
class QAction; class QAction;
class QMenu; class QMenu;
class Menu; class Menu;
class MenuWrapper;
class Bookmarks: public QObject { class Bookmarks: public QObject {
Q_OBJECT Q_OBJECT
@ -26,16 +27,19 @@ class Bookmarks: public QObject {
public: public:
Bookmarks(); Bookmarks();
void setupMenus(const QString & menu); void setupMenus(Menu* menubar, MenuWrapper* menu);
private slots: private slots:
void bookmarkLocation(); void bookmarkLocation();
void teleportToBookmark();
void deleteBookmark(); void deleteBookmark();
private: private:
// FIXME bookmarks should be more categorizable
// Can we leverage a system browser favorites API?
QVariantMap _bookmarks; // { name: address, ... } QVariantMap _bookmarks; // { name: address, ... }
QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction;
const QString BOOKMARKS_FILENAME = "bookmarks.json"; const QString BOOKMARKS_FILENAME = "bookmarks.json";
QString _bookmarksFilename; QString _bookmarksFilename;
@ -47,8 +51,8 @@ private:
void persistToFile(); void persistToFile();
void enableMenuItems(bool enabled); void enableMenuItems(bool enabled);
void addLocationToMenu(QString& name, QString& address); void addLocationToMenu(Menu* menubar, QString& name, QString& address);
void removeLocationFromMenu(QString& name); void removeLocationFromMenu(Menu* menubar, QString& name);
}; };
#endif // hifi_Bookmarks_h #endif // hifi_Bookmarks_h

View file

@ -94,9 +94,9 @@ void MainWindow::changeEvent(QEvent* event) {
emit windowShown(true); emit windowShown(true);
} }
//if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) { if (isFullScreen() != Menu::getInstance()->isOptionChecked(MenuOption::Fullscreen)) {
// Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen()); Menu::getInstance()->setIsOptionChecked(MenuOption::Fullscreen, isFullScreen());
//} }
} else if (event->type() == QEvent::ActivationChange) { } else if (event->type() == QEvent::ActivationChange) {
if (isActiveWindow()) { if (isActiveWindow()) {
emit windowShown(true); emit windowShown(true);

File diff suppressed because it is too large Load diff

View file

@ -18,77 +18,114 @@
#include <QKeySequence> #include <QKeySequence>
#include <QPointer> #include <QPointer>
#include <QStandardPaths> #include <QStandardPaths>
#include <QQuickItem>
#include <MenuItemProperties.h> #include <MenuItemProperties.h>
#include <OffscreenUi.h>
#include "DiscoverabilityManager.h" #include "DiscoverabilityManager.h"
#include <HifiMenu.h>
class Settings; class Settings;
// Proxy object to simplify porting over class MenuWrapper : public QObject {
class HifiAction {
const QString _menuOption;
public: public:
HifiAction(const QString & menuOption); QList<QAction*> actions();
void setCheckable(bool); MenuWrapper* addMenu(const QString& menuName);
void setChecked(bool); void setEnabled(bool enabled = true);
void setVisible(bool); void addSeparator();
QString shortcut() const; void addAction(QAction* action);
void setText(const QString &);
void setTriggerAction(std::function<void()>); QAction* addAction(const QString& menuName);
void setToggleAction(std::function<void(bool)>); void insertAction(QAction* before, QAction* menuName);
QAction* addAction(const QString& menuName, const QObject* receiver, const char* member, const QKeySequence& shortcut = 0);
void removeAction(QAction* action);
QAction* newAction() {
return new QAction(_realMenu);
}
private:
MenuWrapper(QMenu* menu);
static MenuWrapper* fromMenu(QMenu* menu) {
return _backMap[menu];
}
QMenu* const _realMenu;
static QHash<QMenu*, MenuWrapper*> _backMap;
friend class Menu;
}; };
class Menu : public HifiMenu { class Menu : public QMenuBar {
Q_OBJECT Q_OBJECT
public: public:
Menu(QQuickItem * parent = 0);
static Menu* getInstance(); static Menu* getInstance();
// Override the base type HifiMenu with this class instead
static void registerType() {
qmlRegisterType<Menu>("Hifi", 1, 0, NAME.toLocal8Bit().constData());
}
void loadSettings(); void loadSettings();
void saveSettings(); void saveSettings();
MenuWrapper* getMenu(const QString& menuName);
HifiAction * getActionForOption(const QString& menuOption) { void triggerOption(const QString& menuOption);
return new HifiAction(menuOption); QAction* getActionForOption(const QString& menuOption);
}
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const QObject* receiver = NULL,
const char* member = NULL,
QAction::MenuRole role = QAction::NoRole,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
QAction* action,
const QString& actionName = QString(),
const QKeySequence& shortcut = 0,
QAction::MenuRole role = QAction::NoRole,
int menuItemLocation = UNSPECIFIED_POSITION);
void removeAction(MenuWrapper* menu, const QString& actionName);
public slots:
MenuWrapper* addMenu(const QString& menuName);
void removeMenu(const QString& menuName);
bool menuExists(const QString& menuName);
void addSeparator(const QString& menuName, const QString& separatorName);
void removeSeparator(const QString& menuName, const QString& separatorName);
void addMenuItem(const MenuItemProperties& properties); void addMenuItem(const MenuItemProperties& properties);
void removeMenuItem(const QString& menuName, const QString& menuitem);
bool isOptionChecked(const QString& menuOption) const { bool menuItemExists(const QString& menuName, const QString& menuitem);
return HifiMenu::isChecked(menuOption); bool isOptionChecked(const QString& menuOption) const;
} void setIsOptionChecked(const QString& menuOption, bool isChecked);
void setIsOptionChecked(const QString& menuOption, bool isChecked) {
HifiMenu::setChecked(menuOption, isChecked);
}
void triggerOption(const QString& menuOption) {
HifiMenu::triggerItem(menuOption);
}
void setOptionText(const QString& menuOption, const QString & text) {
HifiMenu::setItemText(menuOption, text);
}
void setOptionTriggerAction(const QString& menuOption, std::function<void()> f) {
HifiMenu::setTriggerAction(menuOption, f);
}
private:
void init();
private: private:
static Menu* _instance; static Menu* _instance;
friend class HifiAction; Menu();
typedef void(*settingsAction)(Settings&, QAction&);
static void loadAction(Settings& settings, QAction& action);
static void saveAction(Settings& settings, QAction& action);
void scanMenuBar(settingsAction modifySetting);
void scanMenu(QMenu& menu, settingsAction modifySetting, Settings& settings);
/// helper method to have separators with labels that are also compatible with OS X
void addDisabledActionAndSeparator(MenuWrapper* destinationMenu, const QString& actionName,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* addCheckableActionToQMenuAndActionHash(MenuWrapper* destinationMenu,
const QString& actionName,
const QKeySequence& shortcut = 0,
const bool checked = false,
const QObject* receiver = NULL,
const char* member = NULL,
int menuItemLocation = UNSPECIFIED_POSITION);
QAction* getActionFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getSubMenuFromName(const QString& menuName, MenuWrapper* menu);
MenuWrapper* getMenuParent(const QString& menuName, QString& finalMenuPart);
QAction* getMenuAction(const QString& menuName);
int findPositionOfMenuItem(MenuWrapper* menu, const QString& searchMenuItem);
int positionBeforeSeparatorIfNeeded(MenuWrapper* menu, int requestedPosition);
QHash<QString, QAction*> _actionHash;
}; };
namespace MenuOption { namespace MenuOption {
@ -243,4 +280,5 @@ namespace MenuOption {
const QString VisibleToNoOne = "No one"; const QString VisibleToNoOne = "No one";
const QString Wireframe = "Wireframe"; const QString Wireframe = "Wireframe";
} }
#endif // hifi_Menu_h #endif // hifi_Menu_h

View file

@ -216,7 +216,7 @@ void OculusManager::connect() {
_isConnected = false; _isConnected = false;
// we're definitely not in "VR mode" so tell the menu that // we're definitely not in "VR mode" so tell the menu that
Menu::getInstance()->setIsOptionChecked(MenuOption::EnableVRMode, false); Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false);
} }
} }

View file

@ -28,50 +28,72 @@ void MenuScriptingInterface::menuItemTriggered() {
} }
void MenuScriptingInterface::addMenu(const QString& menu) { void MenuScriptingInterface::addMenu(const QString& menu) {
Menu::getInstance()->addMenu("", menu); QMetaObject::invokeMethod(Menu::getInstance(), "addMenu", Q_ARG(const QString&, menu));
} }
void MenuScriptingInterface::removeMenu(const QString& menu) { void MenuScriptingInterface::removeMenu(const QString& menu) {
Menu::getInstance()->removeMenu(menu); QMetaObject::invokeMethod(Menu::getInstance(), "removeMenu", Q_ARG(const QString&, menu));
} }
bool MenuScriptingInterface::menuExists(const QString& menu) { bool MenuScriptingInterface::menuExists(const QString& menu) {
return Menu::getInstance()->menuExists(menu); bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuExists", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu));
return result;
} }
void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) { void MenuScriptingInterface::addSeparator(const QString& menuName, const QString& separatorName) {
Menu::getInstance()->addSeparator(menuName, separatorName); QMetaObject::invokeMethod(Menu::getInstance(), "addSeparator",
Q_ARG(const QString&, menuName),
Q_ARG(const QString&, separatorName));
} }
void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) { void MenuScriptingInterface::removeSeparator(const QString& menuName, const QString& separatorName) {
Menu::getInstance()->removeSeparator(menuName, separatorName); QMetaObject::invokeMethod(Menu::getInstance(), "removeSeparator",
Q_ARG(const QString&, menuName),
Q_ARG(const QString&, separatorName));
} }
void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) { void MenuScriptingInterface::addMenuItem(const MenuItemProperties& properties) {
Menu::getInstance()->addMenuItem(properties); QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties));
} }
void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) { void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem, const QString& shortcutKey) {
Menu::getInstance()->addItem(menu, menuitem); MenuItemProperties properties(menu, menuitem, shortcutKey);
Menu::getInstance()->setItemShortcut(menuitem, shortcutKey); QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties));
} }
void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) { void MenuScriptingInterface::addMenuItem(const QString& menu, const QString& menuitem) {
Menu::getInstance()->addItem(menu, menuitem); MenuItemProperties properties(menu, menuitem);
QMetaObject::invokeMethod(Menu::getInstance(), "addMenuItem", Q_ARG(const MenuItemProperties&, properties));
} }
void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) { void MenuScriptingInterface::removeMenuItem(const QString& menu, const QString& menuitem) {
Menu::getInstance()->removeItem(menuitem); QMetaObject::invokeMethod(Menu::getInstance(), "removeMenuItem",
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
}; };
bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) { bool MenuScriptingInterface::menuItemExists(const QString& menu, const QString& menuitem) {
return Menu::getInstance()->itemExists(menu, menuitem); bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "menuItemExists", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menu),
Q_ARG(const QString&, menuitem));
return result;
} }
bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) { bool MenuScriptingInterface::isOptionChecked(const QString& menuOption) {
return Menu::getInstance()->isChecked(menuOption); bool result;
QMetaObject::invokeMethod(Menu::getInstance(), "isOptionChecked", Qt::BlockingQueuedConnection,
Q_RETURN_ARG(bool, result),
Q_ARG(const QString&, menuOption));
return result;
} }
void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) { void MenuScriptingInterface::setIsOptionChecked(const QString& menuOption, bool isChecked) {
Menu::getInstance()->setChecked(menuOption, isChecked); QMetaObject::invokeMethod(Menu::getInstance(), "setIsOptionChecked", Qt::BlockingQueuedConnection,
Q_ARG(const QString&, menuOption),
Q_ARG(bool, isChecked));
} }

View file

@ -45,6 +45,7 @@ QScriptValue WindowScriptingInterface::hasFocus() {
} }
void WindowScriptingInterface::setFocus() { void WindowScriptingInterface::setFocus() {
// It's forbidden to call focus() from another thread.
Application::getInstance()->postLambdaEvent([] { Application::getInstance()->postLambdaEvent([] {
auto window = Application::getInstance()->getWindow(); auto window = Application::getInstance()->getWindow();
window->activateWindow(); window->activateWindow();

View file

@ -8,7 +8,6 @@
// Distributed under the Apache License, Version 2.0. // Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <gpu/GPUConfig.h>
#include <QFormLayout> #include <QFormLayout>
#include <QGuiApplication> #include <QGuiApplication>

View file

@ -25,18 +25,19 @@ LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _root
} }
void LoginDialog::toggleAction() { void LoginDialog::toggleAction() {
AccountManager & accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
Menu* menu = Menu::getInstance(); QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login);
Q_CHECK_PTR(loginAction);
disconnect(loginAction, 0, 0, 0);
if (accountManager.isLoggedIn()) { if (accountManager.isLoggedIn()) {
// change the menu item to logout // change the menu item to logout
menu->setOptionText(MenuOption::Login, "Logout " + accountManager.getAccountInfo().getUsername()); loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername());
menu->setOptionTriggerAction(MenuOption::Login, [] { connect(loginAction, &QAction::triggered, &accountManager, &AccountManager::logout);
AccountManager::getInstance().logout();
});
} else { } else {
// change the menu item to login // change the menu item to login
menu->setOptionText(MenuOption::Login, "Login"); loginAction->setText("Login");
menu->setOptionTriggerAction(MenuOption::Login, [] { connect(loginAction, &QAction::triggered, [] {
LoginDialog::show(); LoginDialog::show();
}); });
} }

View file

@ -44,9 +44,9 @@ RunningScriptsWidget::RunningScriptsWidget(QWidget* parent) :
connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset, connect(&_scriptsModelFilter, &QSortFilterProxyModel::modelReset,
this, &RunningScriptsWidget::selectFirstInList); this, &RunningScriptsWidget::selectFirstInList);
// FIXME // FIXME: menu isn't prepared at this point.
// QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText); //QString shortcutText = Menu::getInstance()->getActionForOption(MenuOption::ReloadAllScripts)->shortcut().toString(QKeySequence::NativeText);
// ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts."); //ui->tipLabel->setText("Tip: Use " + shortcutText + " to reload all scripts.");
_scriptsModelFilter.setSourceModel(&_scriptsModel); _scriptsModelFilter.setSourceModel(&_scriptsModel);
_scriptsModelFilter.sort(0, Qt::AscendingOrder); _scriptsModelFilter.sort(0, Qt::AscendingOrder);
@ -162,7 +162,8 @@ void RunningScriptsWidget::showEvent(QShowEvent* event) {
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry(); QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
int topMargin = titleBarHeight; int menuBarHeight = Menu::getInstance()->geometry().height();
int topMargin = titleBarHeight + menuBarHeight;
setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin, setGeometry(parentGeometry.topLeft().x(), parentGeometry.topLeft().y() + topMargin,
size().width(), parentWidget()->height() - topMargin); size().width(), parentWidget()->height() - topMargin);

View file

@ -38,7 +38,8 @@ bool ToolWindow::event(QEvent* event) {
QRect mainGeometry = mainWindow->geometry(); QRect mainGeometry = mainWindow->geometry();
int titleBarHeight = UIUtil::getWindowTitleBarHeight(this); int titleBarHeight = UIUtil::getWindowTitleBarHeight(this);
int topMargin = titleBarHeight; int menuBarHeight = Menu::getInstance()->geometry().height();
int topMargin = titleBarHeight + menuBarHeight;
_lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin, _lastGeometry = QRect(mainGeometry.topLeft().x(), mainGeometry.topLeft().y() + topMargin,
DEFAULT_WIDTH, mainGeometry.height() - topMargin); DEFAULT_WIDTH, mainGeometry.height() - topMargin);

View file

@ -1,47 +1,64 @@
#include "HifiMenu.h" #include "HifiMenu.h"
#include <QtQml> #include <QtQml>
#include <QMenuBar>
// FIXME can this be made a class member? // Binds together a Qt Action or Menu with the QML Menu or MenuItem
static const QString MENU_SUFFIX{ "__Menu" }; class MenuUserData : public QObjectUserData {
static const int USER_DATA_ID;
HIFI_QML_DEF_LAMBDA(HifiMenu, [=](QQmlContext* context, QObject* newItem) { public:
MenuUserData(QAction* action, QObject* qmlObject) {
init(action, qmlObject);
}
MenuUserData(QMenu* menu, QObject* qmlObject) {
init(menu, qmlObject);
}
const QUuid uuid{ QUuid::createUuid() };
static MenuUserData* forObject(QObject* object) {
return static_cast<MenuUserData*>(object->userData(USER_DATA_ID));
}
private:
MenuUserData(const MenuUserData&);
void init(QObject* widgetObject, QObject* qmlObject) {
widgetObject->setUserData(USER_DATA_ID, this);
qmlObject->setUserData(USER_DATA_ID, this);
qmlObject->setObjectName(uuid.toString());
// Make sure we can find it again in the future
Q_ASSERT(VrMenu::instance()->findMenuObject(uuid.toString()));
}
};
const int MenuUserData::USER_DATA_ID = QObject::registerUserData();
HIFI_QML_DEF_LAMBDA(VrMenu, [&](QQmlContext* context, QObject* newItem) {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); auto offscreenUi = DependencyManager::get<OffscreenUi>();
QObject * rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu"); QObject * rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("rootMenu");
Q_ASSERT(rootMenu); Q_ASSERT(rootMenu);
static_cast<HifiMenu*>(newItem)->setRootMenu(rootMenu); static_cast<VrMenu*>(newItem)->setRootMenu(rootMenu);
context->setContextProperty("rootMenu", rootMenu); context->setContextProperty("rootMenu", rootMenu);
}); });
HifiMenu::HifiMenu(QQuickItem* parent) : QQuickItem(parent), _triggerMapper(this), _toggleMapper(this) { VrMenu* VrMenu::_instance{ nullptr };
VrMenu* VrMenu::instance() {
if (!_instance) {
VrMenu::registerType();
VrMenu::load();
Q_ASSERT(_instance);
}
return _instance;
}
VrMenu::VrMenu(QQuickItem* parent) : QQuickItem(parent) {
_instance = this;
this->setEnabled(false); this->setEnabled(false);
connect(&_triggerMapper, SIGNAL(mapped(QString)), this, SLOT(onTriggeredByName(const QString &)));
connect(&_toggleMapper, SIGNAL(mapped(QString)), this, SLOT(onToggledByName(const QString &)));
}
void HifiMenu::onTriggeredByName(const QString & name) {
qDebug() << name << " triggered";
if (_triggerActions.count(name)) {
_triggerActions[name]();
}
}
void HifiMenu::onToggledByName(const QString & name) {
qDebug() << name << " toggled";
if (_toggleActions.count(name)) {
QObject* menu = findMenuObject(name);
bool checked = menu->property("checked").toBool();
_toggleActions[name](checked);
}
}
void HifiMenu::setToggleAction(const QString & name, std::function<void(bool)> f) {
_toggleActions[name] = f;
}
void HifiMenu::setTriggerAction(const QString & name, std::function<void()> f) {
_triggerActions[name] = f;
} }
// QML helper functions
QObject* addMenu(QObject* parent, const QString & text) { QObject* addMenu(QObject* parent, const QString & text) {
// FIXME add more checking here to ensure no name conflicts // FIXME add more checking here to ensure no name conflicts
QVariant returnedValue; QVariant returnedValue;
@ -50,7 +67,7 @@ QObject* addMenu(QObject* parent, const QString & text) {
Q_ARG(QVariant, text)); Q_ARG(QVariant, text));
QObject* result = returnedValue.value<QObject*>(); QObject* result = returnedValue.value<QObject*>();
if (result) { if (result) {
result->setObjectName(text + MENU_SUFFIX); result->setObjectName(text);
} }
return result; return result;
} }
@ -67,236 +84,109 @@ QObject* addItem(QObject* parent, const QString& text) {
return result; return result;
} }
const QObject* HifiMenu::findMenuObject(const QString & menuOption) const { const QObject* VrMenu::findMenuObject(const QString & menuOption) const {
if (menuOption.isEmpty()) { if (menuOption.isEmpty()) {
return _rootMenu; return _rootMenu;
} }
const QObject* result = _rootMenu->findChild<QObject*>(menuOption + MENU_SUFFIX); const QObject* result = _rootMenu->findChild<QObject*>(menuOption);
return result; return result;
} }
QObject* HifiMenu::findMenuObject(const QString & menuOption) { QObject* VrMenu::findMenuObject(const QString & menuOption) {
if (menuOption.isEmpty()) { if (menuOption.isEmpty()) {
return _rootMenu; return _rootMenu;
} }
QObject* result = _rootMenu->findChild<QObject*>(menuOption + MENU_SUFFIX); QObject* result = _rootMenu->findChild<QObject*>(menuOption);
return result; return result;
} }
void HifiMenu::addMenu(const QString & parentMenu, const QString & menuOption) { void VrMenu::setRootMenu(QObject* rootMenu) {
QObject* parent = findMenuObject(parentMenu);
QObject* result = ::addMenu(parent, menuOption);
Q_ASSERT(result);
result->setObjectName(menuOption + MENU_SUFFIX);
Q_ASSERT(findMenuObject(menuOption));
}
void HifiMenu::removeMenu(const QString& menuName) {
QObject* menu = findMenuObject(menuName);
Q_ASSERT(menu);
Q_ASSERT(menu != _rootMenu);
QMetaObject::invokeMethod(menu->parent(), "removeItem",
Q_ARG(QVariant, QVariant::fromValue(menu)));
}
bool HifiMenu::menuExists(const QString& menuName) const {
return findMenuObject(menuName);
}
void HifiMenu::addSeparator(const QString& parentMenu, const QString& separatorName) {
QObject * parent = findMenuObject(parentMenu);
bool invokeResult = QMetaObject::invokeMethod(parent, "addSeparator", Qt::DirectConnection);
Q_ASSERT(invokeResult);
addItem(parentMenu, separatorName);
enableItem(separatorName, false);
}
void HifiMenu::removeSeparator(const QString& parentMenu, const QString& separatorName) {
}
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption) {
QObject* parent = findMenuObject(parentMenu);
Q_ASSERT(parent);
QObject* result = ::addItem(parent, menuOption);
Q_ASSERT(result);
result->setObjectName(menuOption + MENU_SUFFIX);
Q_ASSERT(findMenuObject(menuOption));
_triggerMapper.setMapping(result, menuOption);
connect(result, SIGNAL(triggered()), &_triggerMapper, SLOT(map()));
_toggleMapper.setMapping(result, menuOption);
connect(result, SIGNAL(toggled(bool)), &_toggleMapper, SLOT(map()));
}
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, std::function<void()> f) {
setTriggerAction(menuOption, f);
addItem(parentMenu, menuOption);
}
void HifiMenu::addItem(const QString & parentMenu, const QString & menuOption, QObject* receiver, const char* slot) {
addItem(parentMenu, menuOption);
connectItem(menuOption, receiver, slot);
}
void HifiMenu::removeItem(const QString& menuOption) {
removeMenu(menuOption);
}
bool HifiMenu::itemExists(const QString& menuName, const QString& menuitem) const {
return findMenuObject(menuName);
}
void HifiMenu::triggerItem(const QString& menuOption) {
QObject* menuItem = findMenuObject(menuOption);
Q_ASSERT(menuItem);
Q_ASSERT(menuItem != _rootMenu);
QMetaObject::invokeMethod(menuItem, "trigger");
}
QHash<QString, QString> warned;
void warn(const QString & menuOption) {
if (!warned.contains(menuOption)) {
warned[menuOption] = menuOption;
qWarning() << "No menu item: " << menuOption;
}
}
bool HifiMenu::isChecked(const QString& menuOption) const {
const QObject* menuItem = findMenuObject(menuOption);
if (!menuItem) {
warn(menuOption);
return false;
}
return menuItem->property("checked").toBool();
}
void HifiMenu::setChecked(const QString& menuOption, bool isChecked) {
QObject* menuItem = findMenuObject(menuOption);
if (!menuItem) {
warn(menuOption);
return;
}
if (menuItem->property("checked").toBool() != isChecked) {
menuItem->setProperty("checked", QVariant::fromValue(isChecked));
Q_ASSERT(menuItem->property("checked").toBool() == isChecked);
}
}
void HifiMenu::setCheckable(const QString& menuOption, bool checkable) {
QObject* menuItem = findMenuObject(menuOption);
if (!menuItem) {
warn(menuOption);
return;
}
menuItem->setProperty("checkable", QVariant::fromValue(checkable));
Q_ASSERT(menuItem->property("checkable").toBool() == checkable);
}
void HifiMenu::setItemText(const QString& menuOption, const QString& text) {
QObject* menuItem = findMenuObject(menuOption);
if (!menuItem) {
warn(menuOption);
return;
}
if (menuItem->property("type").toInt() == 2) {
menuItem->setProperty("title", QVariant::fromValue(text));
} else {
menuItem->setProperty("text", QVariant::fromValue(text));
}
}
void HifiMenu::setRootMenu(QObject* rootMenu) {
_rootMenu = rootMenu; _rootMenu = rootMenu;
} }
void HifiMenu::enableItem(const QString & menuOption, bool enabled) { void VrMenu::addMenu(QMenu* menu) {
QObject* menuItem = findMenuObject(menuOption); Q_ASSERT(!MenuUserData::forObject(menu));
if (!menuItem) { QObject * parent = menu->parent();
warn(menuOption); QObject * qmlParent;
return; if (dynamic_cast<QMenu*>(parent)) {
MenuUserData* userData = MenuUserData::forObject(parent);
qmlParent = findMenuObject(userData->uuid.toString());
} else if (dynamic_cast<QMenuBar*>(parent)) {
qmlParent = _rootMenu;
} else {
Q_ASSERT(false);
} }
menuItem->setProperty("enabled", QVariant::fromValue(enabled)); QObject* result = ::addMenu(qmlParent, menu->title());
new MenuUserData(menu, result);
} }
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked) { void updateQmlItemFromAction(QObject* target, QAction* source) {
addItem(parentMenu, menuOption); target->setProperty("checkable", source->isCheckable());
setCheckable(menuOption); target->setProperty("enabled", source->isEnabled());
if (checked) { target->setProperty("visible", source->isVisible());
setChecked(menuOption, checked); target->setProperty("text", source->text());
} target->setProperty("checked", source->isChecked());
} }
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function<void(bool)> f) { void bindActionToQmlAction(QObject* qmlAction, QAction* action) {
setToggleAction(menuOption, f); new MenuUserData(action, qmlAction);
addCheckableItem(parentMenu, menuOption, checked); updateQmlItemFromAction(qmlAction, action);
QObject::connect(action, &QAction::changed, [=] {
updateQmlItemFromAction(qmlAction, action);
});
QObject::connect(action, &QAction::toggled, [=](bool checked) {
qmlAction->setProperty("checked", checked);
});
QObject::connect(qmlAction, SIGNAL(triggered()), action, SLOT(trigger()));
} }
void HifiMenu::setItemVisible(const QString& menuOption, bool visible) { void VrMenu::addAction(QMenu* menu, QAction* action) {
QObject* result = findMenuObject(menuOption); Q_ASSERT(!MenuUserData::forObject(action));
if (result) { Q_ASSERT(MenuUserData::forObject(menu));
result->setProperty("visible", visible); MenuUserData* userData = MenuUserData::forObject(menu);
} QObject* parent = findMenuObject(userData->uuid.toString());
} Q_ASSERT(parent);
QObject* result = ::addItem(parent, action->text());
bool HifiMenu::isItemVisible(const QString& menuOption) {
QObject* result = findMenuObject(menuOption);
if (result) {
return result->property("visible").toBool();
}
return false;
}
void HifiMenu::setItemShortcut(const QString& menuOption, const QString& shortcut) {
QObject* result = findMenuObject(menuOption);
if (result) {
result->setProperty("shortcut", shortcut);
}
}
QString HifiMenu::getItemShortcut(const QString& menuOption) {
QObject* result = findMenuObject(menuOption);
if (result) {
return result->property("shortcut").toString();
}
return QString();
}
void HifiMenu::addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot) {
addCheckableItem(parentMenu, menuOption, checked);
connectItem(menuOption, receiver, slot);
}
void HifiMenu::connectCheckable(const QString& menuOption, QObject* receiver, const char* slot) {
QObject* result = findMenuObject(menuOption);
connect(result, SIGNAL(toggled(bool)), receiver, slot);
}
void HifiMenu::connectItem(const QString& menuOption, QObject* receiver, const char* slot) {
QObject* result = findMenuObject(menuOption);
connect(result, SIGNAL(triggered()), receiver, slot);
}
void HifiMenu::setExclusiveGroup(const QString& menuOption, const QString& groupName) {
static const QString GROUP_SUFFIX{ "__Group" };
auto offscreenUi = DependencyManager::get<OffscreenUi>();
QObject* group = offscreenUi->getRootItem()->findChild<QObject*>(groupName + GROUP_SUFFIX);
if (!group) {
group = offscreenUi->load("ExclusiveGroup.qml");
Q_ASSERT(group);
}
QObject* menuItem = findMenuObject(menuOption);
bool result = menuItem->setProperty("text", QVariant::fromValue(group));
Q_ASSERT(result); Q_ASSERT(result);
// Bind the QML and Widget together
bindActionToQmlAction(result, action);
} }
bool HifiMenu::connectAction(int action, QObject * receiver, const char * slot) { void VrMenu::insertAction(QAction* before, QAction* action) {
auto offscreenUi = DependencyManager::get<OffscreenUi>(); QObject* beforeQml{ nullptr };
QObject* rootMenu = offscreenUi->getRootItem()->findChild<QObject*>("AllActions"); {
QString name = "HifiAction_" + QVariant(action).toString(); MenuUserData* beforeUserData = MenuUserData::forObject(before);
QObject* quitAction = rootMenu->findChild<QObject*>(name); Q_ASSERT(beforeUserData);
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit())); beforeQml = findMenuObject(beforeUserData->uuid.toString());
return true; }
QObject* menu = beforeQml->parent();
int index{ -1 };
QVariant itemsVar = menu->property("items");
QList<QVariant> items = itemsVar.toList();
// FIXME add more checking here to ensure no name conflicts
for (index = 0; index < items.length(); ++index) {
QObject* currentQmlItem = items.at(index).value<QObject*>();
if (currentQmlItem == beforeQml) {
break;
}
}
QObject* result{ nullptr };
if (index < 0 || index >= items.length()) {
result = ::addItem(menu, action->text());
} else {
QQuickMenuItem* returnedValue{ nullptr };
bool invokeResult = QMetaObject::invokeMethod(menu, "insertItem", Qt::DirectConnection,
Q_RETURN_ARG(QQuickMenuItem*, returnedValue),
Q_ARG(int, index), Q_ARG(QString, action->text()));
Q_ASSERT(invokeResult);
result = reinterpret_cast<QObject*>(returnedValue);
}
Q_ASSERT(result);
bindActionToQmlAction(result, action);
} }
void VrMenu::removeAction(QAction* action) {
// FIXME implement
}

View file

@ -16,67 +16,32 @@
#include <QHash> #include <QHash>
#include <QList> #include <QList>
#include <QSignalMapper> #include <QSignalMapper>
#include <QAction>
#include <QMenu>
#include "OffscreenUi.h" #include "OffscreenUi.h"
class HifiMenu : public QQuickItem { class VrMenu : public QQuickItem {
Q_OBJECT Q_OBJECT
HIFI_QML_DECL_LAMBDA HIFI_QML_DECL_LAMBDA
public: public:
static VrMenu* instance();
static bool connectAction(int action, QObject * receiver, const char * slot); VrMenu(QQuickItem* parent = nullptr);
void addMenu(QMenu* menu);
HifiMenu(QQuickItem* parent = nullptr); void addAction(QMenu* parent, QAction* action);
void insertAction(QAction* before, QAction* action);
void setToggleAction(const QString& name, std::function<void(bool)> f); void removeAction(QAction* action);
void setTriggerAction(const QString& name, std::function<void()> f);
void addMenu(const QString& parentMenu, const QString& menuOption);
void removeMenu(const QString& menuName);
bool menuExists(const QString& menuName) const;
void addSeparator(const QString& menuName, const QString& separatorName);
void removeSeparator(const QString& menuName, const QString& separatorName);
void addItem(const QString& parentMenu, const QString& menuOption);
void addItem(const QString& parentMenu, const QString& menuOption, std::function<void()> f);
void addItem(const QString& parentMenu, const QString& menuOption, QObject* receiver, const char* slot);
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked = false);
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, std::function<void(bool)> f);
void addCheckableItem(const QString& parentMenu, const QString& menuOption, bool checked, QObject* receiver, const char* slot);
void connectCheckable(const QString& menuOption, QObject* receiver, const char* slot);
void connectItem(const QString& menuOption, QObject* receiver, const char* slot);
void removeItem(const QString& menuitem);
bool itemExists(const QString& menuName, const QString& menuitem) const;
void triggerItem(const QString& menuOption);
void enableItem(const QString& menuOption, bool enabled = true);
bool isChecked(const QString& menuOption) const;
void setChecked(const QString& menuOption, bool checked = true);
void setCheckable(const QString& menuOption, bool checkable = true);
void setExclusiveGroup(const QString& menuOption, const QString& groupName);
void setItemText(const QString& menuOption, const QString& text);
void setItemVisible(const QString& menuOption, bool visible = true);
bool isItemVisible(const QString& menuOption);
void setItemShortcut(const QString& menuOption, const QString& shortcut);
QString getItemShortcut(const QString& menuOption);
void setRootMenu(QObject* rootMenu); void setRootMenu(QObject* rootMenu);
private slots:
void onTriggeredByName(const QString& name);
void onToggledByName(const QString& name);
protected: protected:
QHash<QString, std::function<void()>> _triggerActions; QObject* _rootMenu{ nullptr };
QHash<QString, std::function<void(bool)>> _toggleActions;
QObject* findMenuObject(const QString& name); QObject* findMenuObject(const QString& name);
const QObject* findMenuObject(const QString& name) const; const QObject* findMenuObject(const QString& name) const;
QObject* _rootMenu{ nullptr };
QSignalMapper _triggerMapper; static VrMenu* _instance;
QSignalMapper _toggleMapper; friend class MenuUserData;
}; };
#endif // hifi_MenuConstants_h #endif // hifi_MenuConstants_h

View file

@ -366,10 +366,14 @@ bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event); QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QPointF originalPos = mouseEvent->localPos(); QPointF originalPos = mouseEvent->localPos();
QPointF transformedPos = _mouseTranslator(originalPos); QPointF transformedPos = _mouseTranslator(originalPos);
transformedPos = mapWindowToUi(transformedPos, originalDestination);
QMouseEvent mappedEvent(mouseEvent->type(), QMouseEvent mappedEvent(mouseEvent->type(),
mapWindowToUi(transformedPos, originalDestination), transformedPos,
mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->screenPos(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers()); mouseEvent->buttons(), mouseEvent->modifiers());
if (event->type() == QEvent::MouseMove) {
_qmlEngine->rootContext()->setContextProperty("lastMousePosition", transformedPos);
}
mappedEvent.ignore(); mappedEvent.ignore();
if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) { if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {
return mappedEvent.isAccepted(); return mappedEvent.isAccepted();
@ -429,6 +433,7 @@ void OffscreenUi::toggle(const QUrl& url, const QString& name, std::function<voi
item = _rootItem->findChild<QQuickItem*>(name); item = _rootItem->findChild<QQuickItem*>(name);
} }
if (item) { if (item) {
qDebug() << "Turning item " << !item->isEnabled();
item->setEnabled(!item->isEnabled()); item->setEnabled(!item->isEnabled());
} }
} }

View file

@ -320,7 +320,7 @@ public:
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
MessageDialog::registerType(); MessageDialog::registerType();
HifiMenu::registerType(); VrMenu::registerType();
qmlRegisterType<MenuConstants>("Hifi", 1, 0, "MenuConstants"); qmlRegisterType<MenuConstants>("Hifi", 1, 0, "MenuConstants");
@ -348,10 +348,9 @@ public:
#else #else
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir())); offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
offscreenUi->load(QUrl("TestRoot.qml")); offscreenUi->load(QUrl("TestRoot.qml"));
offscreenUi->load(QUrl("Menu.qml")); offscreenUi->load(QUrl("InterfaceMenu.qml"));
// Requires a root menu to have been loaded before it can load // Requires a root menu to have been loaded before it can load
HifiMenu::load(); VrMenu::load();
HifiMenu::connectAction(MenuConstants::Quit, qApp, SLOT(quit()));
#endif #endif
installEventFilter(offscreenUi.data()); installEventFilter(offscreenUi.data());
offscreenUi->resume(); offscreenUi->resume();
@ -407,10 +406,6 @@ protected:
switch (event->key()) { switch (event->key()) {
case Qt::Key_L: case Qt::Key_L:
if (event->modifiers() & Qt::CTRL) { if (event->modifiers() & Qt::CTRL) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
HifiMenu * menu = offscreenUi->findChild<HifiMenu*>();
menu->addItem("", "Test 3");
menu->addItem("File", "Test 3");
} }
break; break;
case Qt::Key_K: case Qt::Key_K:
@ -433,7 +428,7 @@ protected:
QQmlContext* menuContext{ nullptr }; QQmlContext* menuContext{ nullptr };
void keyReleaseEvent(QKeyEvent *event) { void keyReleaseEvent(QKeyEvent *event) {
if (_altPressed && Qt::Key_Alt == event->key()) { if (_altPressed && Qt::Key_Alt == event->key()) {
HifiMenu::toggle(); VrMenu::toggle();
} }
} }