diff --git a/interface/resources/qml/AssetServer.qml b/interface/resources/qml/AssetServer.qml index c85fd5b379..bf46ba121c 100644 --- a/interface/resources/qml/AssetServer.qml +++ b/interface/resources/qml/AssetServer.qml @@ -542,7 +542,7 @@ ScrollingWindow { Item { height: parent.height width: parent.width - HifiControls.Button { + HifiControls.QueuedButton { id: uploadButton anchors.right: parent.right @@ -552,22 +552,7 @@ ScrollingWindow { height: 30 width: 155 - onClicked: uploadClickedTimer.running = true - - // For some reason trigginer an API that enters - // an internal event loop directly from the button clicked - // trigger below causes the appliction to behave oddly. - // Most likely because the button onClicked handling is never - // completed until the function returns. - // FIXME find a better way of handling the input dialogs that - // doesn't trigger this. - Timer { - id: uploadClickedTimer - interval: 5 - repeat: false - running: false - onTriggered: uploadClicked(); - } + onClickedQueued: uploadClicked() } Item { diff --git a/interface/resources/qml/controls-uit/QueuedButton.qml b/interface/resources/qml/controls-uit/QueuedButton.qml new file mode 100644 index 0000000000..36ffbe582f --- /dev/null +++ b/interface/resources/qml/controls-uit/QueuedButton.qml @@ -0,0 +1,43 @@ +// +// QueuedButton.qml +// -- original Button.qml + signal timer workaround --ht +// Created by David Rowe on 16 Feb 2016 +// Copyright 2016 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 +// + +import QtQuick 2.5 +import QtQuick.Controls 1.4 as Original +import QtQuick.Controls.Styles 1.4 + +import "../styles-uit" +import "." as HifiControls + +HifiControls.Button { + // FIXME: THIS WORKAROUND MIGRATED/CONSOLIDATED FROM RUNNINGSCRIPTS.QML + + // For some reason trigginer an API that enters + // an internal event loop directly from the button clicked + // trigger below causes the appliction to behave oddly. + // Most likely because the button onClicked handling is never + // completed until the function returns. + // FIXME find a better way of handling the input dialogs that + // doesn't trigger this. + + // NOTE: dialogs that need to use this workaround can connect via + // onQueuedClicked: ... + // instead of: + // onClicked: ... + + signal clickedQueued() + Timer { + id: fromTimer + interval: 5 + repeat: false + running: false + onTriggered: clickedQueued() + } + onClicked: fromTimer.running = true +} diff --git a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml index 7ae4fe6761..bcc5a1d9e6 100644 --- a/interface/resources/qml/dialogs/preferences/AvatarPreference.qml +++ b/interface/resources/qml/dialogs/preferences/AvatarPreference.qml @@ -16,7 +16,6 @@ import "../../hifi/tablet/tabletWindows/preferences" Preference { id: root property alias text: dataTextField.text - property alias buttonText: button.text property alias placeholderText: dataTextField.placeholderText property var browser; height: control.height + hifi.dimensions.controlInterlineHeight @@ -58,28 +57,46 @@ Preference { right: parent.right bottom: parent.bottom } - height: Math.max(dataTextField.controlHeight, button.height) + height: dataTextField.controlHeight + bookmarkAvatarButton.height + hifi.dimensions.contentSpacing.y TextField { id: dataTextField + label: root.label placeholderText: root.placeholderText text: preference.value - label: root.label + colorScheme: dataTextField.acceptableInput ? hifi.colorSchemes.dark : hifi.colorSchemes.light + validator: RegExpValidator { + regExp: /.*\.(?:fst).*\?*/ig + } anchors { left: parent.left - right: button.left - rightMargin: hifi.dimensions.contentSpacing.x - bottom: parent.bottom + right: parent.right + bottom: bookmarkAvatarButton.top + bottomMargin: hifi.dimensions.contentSpacing.y } - colorScheme: hifi.colorSchemes.dark + } + + QueuedButton { + id: bookmarkAvatarButton + text: "Bookmark Avatar" + width: 140 + visible: dataTextField.acceptableInput + anchors { + left: parent.left + bottom: parent.bottom + rightMargin: hifi.dimensions.contentSpacing.x + } + onClickedQueued: ApplicationInterface.loadAddAvatarBookmarkDialog() } Button { - id: button - text: "Browse" + id: browseAvatarsButton + text: "Browse Avatars" + width: 140 anchors { - right: parent.right - verticalCenter: dataTextField.verticalCenter + left: dataTextField.acceptableInput ? bookmarkAvatarButton.right : parent.left + bottom: parent.bottom + leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0 } onClicked: { if (typeof desktop !== "undefined") { @@ -103,5 +120,6 @@ Preference { eventBridge: tabletRoot.eventBridge } } + } } diff --git a/interface/resources/qml/hifi/dialogs/RunningScripts.qml b/interface/resources/qml/hifi/dialogs/RunningScripts.qml index 29807d9646..d95dbc2e55 100644 --- a/interface/resources/qml/hifi/dialogs/RunningScripts.qml +++ b/interface/resources/qml/hifi/dialogs/RunningScripts.qml @@ -219,41 +219,18 @@ ScrollingWindow { Row { spacing: hifi.dimensions.contentSpacing.x - HifiControls.Button { + HifiControls.QueuedButton { text: "from URL" color: hifi.buttons.black height: 26 - onClicked: fromUrlTimer.running = true - - // For some reason trigginer an API that enters - // an internal event loop directly from the button clicked - // trigger below causes the appliction to behave oddly. - // Most likely because the button onClicked handling is never - // completed until the function returns. - // FIXME find a better way of handling the input dialogs that - // doesn't trigger this. - Timer { - id: fromUrlTimer - interval: 5 - repeat: false - running: false - onTriggered: ApplicationInterface.loadScriptURLDialog(); - } + onClickedQueued: ApplicationInterface.loadScriptURLDialog() } - HifiControls.Button { + HifiControls.QueuedButton { text: "from Disk" color: hifi.buttons.black height: 26 - onClicked: fromDiskTimer.running = true - - Timer { - id: fromDiskTimer - interval: 5 - repeat: false - running: false - onTriggered: ApplicationInterface.loadDialog(); - } + onClickedQueued: ApplicationInterface.loadDialog() } HifiControls.Button { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 88278034d0..0d3d8ece8d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -59,6 +59,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ #include #include #include +#include "LocationBookmarks.h" #include #include #include @@ -530,6 +532,8 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(nullptr, qApp->getOcteeSceneStats()); + DependencyManager::set(); + DependencyManager::set(); return previousSessionCrashed; } @@ -703,8 +707,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo usleep(USECS_PER_MSEC * 50); // 20hz } - _bookmarks = new Bookmarks(); // Before setting up the menu - // start the nodeThread so its event loop is running QThread* nodeThread = new QThread(this); nodeThread->setObjectName("NodeList Thread"); @@ -2009,6 +2011,8 @@ void Application::initializeUi() { rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get().data()); rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); + rootContext->setContextProperty("AvatarBookmarks", DependencyManager::get().data()); + rootContext->setContextProperty("LocationBookmarks", DependencyManager::get().data()); // Caches rootContext->setContextProperty("AnimationCache", DependencyManager::get().data()); @@ -5494,6 +5498,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get()->getStats().data()); scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get().data()); + scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get().data()); // Caches scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get().data()); @@ -6359,7 +6365,6 @@ void Application::loadLODToolsDialog() { } else { tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml"); } - } @@ -6409,6 +6414,11 @@ void Application::toggleEntityScriptServerLogDialog() { } } +void Application::loadAddAvatarBookmarkDialog() const { + auto avatarBookmarks = DependencyManager::get(); + avatarBookmarks->addBookmark(); +} + void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { postLambdaEvent([notify, includeAnimated, aspectRatio, this] { QMediaPlayer* player = new QMediaPlayer(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 44c0f127c2..cf79a2dfd8 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -52,7 +52,6 @@ #include "avatar/MyAvatar.h" #include "BandwidthRecorder.h" -#include "Bookmarks.h" #include "Camera.h" #include "ConnectionMonitor.h" #include "gpu/Context.h" @@ -261,7 +260,6 @@ public: glm::mat4 getEyeProjection(int eye) const; QRect getDesirableApplicationGeometry() const; - Bookmarks* getBookmarks() const { return _bookmarks; } virtual bool canAcceptURL(const QString& url) const override; virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; @@ -330,6 +328,7 @@ public slots: void toggleEntityScriptServerLogDialog(); void toggleRunningScriptsWidget() const; Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); + Q_INVOKABLE void loadAddAvatarBookmarkDialog() const; void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const; @@ -599,8 +598,6 @@ private: bool _aboutToQuit; - Bookmarks* _bookmarks; - bool _notifiedPacketVersionMismatchThisDomain; QThread _settingsThread; diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp new file mode 100644 index 0000000000..5cdfc8213f --- /dev/null +++ b/interface/src/AvatarBookmarks.cpp @@ -0,0 +1,81 @@ +// +// AvatarBookmarks.cpp +// interface/src +// +// Created by Triplelexx on 23/03/17. +// Copyright 2017 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 +#include +#include +#include +#include + +#include +#include +#include + +#include "MainWindow.h" +#include "Menu.h" + +#include "AvatarBookmarks.h" +#include + +AvatarBookmarks::AvatarBookmarks() { + _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + AVATARBOOKMARKS_FILENAME; + readFromFile(); +} + +void AvatarBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { + // Add menus/actions + auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkAvatar); + QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection); + _bookmarksMenu = menu->addMenu(MenuOption::AvatarBookmarks); + _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteAvatarBookmark); + QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); + + Bookmarks::setupMenus(menubar, menu); + Bookmarks::sortActions(menubar, _bookmarksMenu); +} + +void AvatarBookmarks::changeToBookmarkedAvatar() { + QAction* action = qobject_cast(sender()); + const QString& address = action->data().toString(); + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + myAvatar->useFullAvatarURL(address); +} + +void AvatarBookmarks::addBookmark() { + bool ok = false; + auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Avatar", "Name", QString(), &ok); + if (!ok) { + return; + } + + bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + if (bookmarkName.length() == 0) { + return; + } + + auto myAvatar = DependencyManager::get()->getMyAvatar(); + const QString& bookmarkAddress = myAvatar->getSkeletonModelURL().toString(); + Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); +} + +void AvatarBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) { + QAction* changeAction = _bookmarksMenu->newAction(); + changeAction->setData(address); + connect(changeAction, SIGNAL(triggered()), this, SLOT(changeToBookmarkedAvatar())); + if (!_isMenuSorted) { + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole); + } else { + // TODO: this is aggressive but other alternatives have proved less fruitful so far. + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, changeAction, name, 0, QAction::NoRole); + Bookmarks::sortActions(menubar, _bookmarksMenu); + } +} diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h new file mode 100644 index 0000000000..725af88b0d --- /dev/null +++ b/interface/src/AvatarBookmarks.h @@ -0,0 +1,40 @@ +// +// AvatarBookmarks.h +// interface/src +// +// Created by Triplelexx on 23/03/17. +// Copyright 2017 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 +// + +#ifndef hifi_AvatarBookmarks_h +#define hifi_AvatarBookmarks_h + +#include +#include "Bookmarks.h" + +class AvatarBookmarks: public Bookmarks, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + AvatarBookmarks(); + + void setupMenus(Menu* menubar, MenuWrapper* menu) override; + +public slots: + void addBookmark(); + +protected: + void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override; + +private: + const QString AVATARBOOKMARKS_FILENAME = "avatarbookmarks.json"; + +private slots: + void changeToBookmarkedAvatar(); +}; + +#endif // hifi_AvatarBookmarks_h diff --git a/interface/src/Bookmarks.cpp b/interface/src/Bookmarks.cpp index 476925a0c5..4cbc17f8ce 100644 --- a/interface/src/Bookmarks.cpp +++ b/interface/src/Bookmarks.cpp @@ -11,14 +11,10 @@ #include #include -#include -#include #include -#include #include #include -#include #include #include @@ -27,15 +23,69 @@ #include "InterfaceLogging.h" #include "Bookmarks.h" -#include +Bookmarks::Bookmarks() : +_isMenuSorted(false) +{ +} -const QString Bookmarks::HOME_BOOKMARK = "Home"; +void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { + // Enable/Disable menus as needed + enableMenuItems(_bookmarks.count() > 0); + // Load Bookmarks + for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it) { + QString bookmarkName = it.key(); + QString bookmarkAddress = it.value().toString(); + addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress); + } +} -Bookmarks::Bookmarks() { - _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME; - readFromFile(); +void Bookmarks::deleteBookmark() { + QStringList bookmarkList; + QList menuItems = _bookmarksMenu->actions(); + for (int i = 0; i < menuItems.count(); ++i) { + bookmarkList.append(menuItems[i]->text()); + } + + bool ok = false; + auto bookmarkName = OffscreenUi::getItem(OffscreenUi::ICON_PLACEMARK, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok); + if (!ok) { + return; + } + + bookmarkName = bookmarkName.trimmed(); + if (bookmarkName.length() == 0) { + return; + } + + removeBookmarkFromMenu(Menu::getInstance(), bookmarkName); + remove(bookmarkName); + + if (_bookmarksMenu->actions().count() == 0) { + enableMenuItems(false); + } +} + +void Bookmarks::addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress) { + Menu* menubar = Menu::getInstance(); + if (contains(bookmarkName)) { + auto offscreenUi = DependencyManager::get(); + auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark", + "The bookmark name you entered already exists in your list.", + QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?"); + + auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage); + if (result != QMessageBox::Yes) { + return; + } + removeBookmarkFromMenu(menubar, bookmarkName); + } + + addBookmarkToMenu(menubar, bookmarkName, bookmarkAddress); + insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. + enableMenuItems(true); } void Bookmarks::insert(const QString& name, const QString& address) { @@ -64,6 +114,33 @@ bool Bookmarks::contains(const QString& name) const { return _bookmarks.contains(name); } +bool Bookmarks::sortOrder(QAction* a, QAction* b) { + return a->text().toLower().localeAwareCompare(b->text().toLower()) < 0; +} + +void Bookmarks::sortActions(Menu* menubar, MenuWrapper* menu) { + QList actions = menu->actions(); + qSort(actions.begin(), actions.end(), sortOrder); + for (QAction* action : menu->actions()) { + menu->removeAction(action); + } + for (QAction* action : actions) { + menu->addAction(action); + } + _isMenuSorted = true; +} + +int Bookmarks::getMenuItemLocation(QList actions, const QString& name) const { + int menuItemLocation = 0; + for (QAction* action : actions) { + if (name.toLower().localeAwareCompare(action->text().toLower()) < 0) { + menuItemLocation = actions.indexOf(action); + break; + } + } + return menuItemLocation; +} + QString Bookmarks::addressForBookmark(const QString& name) const { return _bookmarks.value(name).toString(); } @@ -99,108 +176,6 @@ void Bookmarks::persistToFile() { saveFile.write(data); } -void Bookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { - // Add menus/actions - auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation); - QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(bookmarkLocation()), Qt::QueuedConnection); - auto setHomeAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::SetHomeLocation); - QObject::connect(setHomeAction, SIGNAL(triggered()), this, SLOT(setHomeLocation()), Qt::QueuedConnection); - _bookmarksMenu = menu->addMenu(MenuOption::Bookmarks); - _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark); - QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); - - // Enable/Disable menus as needed - enableMenuItems(_bookmarks.count() > 0); - - // Load bookmarks - for (auto it = _bookmarks.begin(); it != _bookmarks.end(); ++it ) { - QString bookmarkName = it.key(); - QString bookmarkAddress = it.value().toString(); - addLocationToMenu(menubar, bookmarkName, bookmarkAddress); - } -} - -void Bookmarks::bookmarkLocation() { - bool ok = false; - auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok); - if (!ok) { - return; - } - - bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); - if (bookmarkName.length() == 0) { - return; - } - - auto addressManager = DependencyManager::get(); - QString bookmarkAddress = addressManager->currentAddress().toString(); - - Menu* menubar = Menu::getInstance(); - if (contains(bookmarkName)) { - auto offscreenUi = DependencyManager::get(); - auto duplicateBookmarkMessage = offscreenUi->createMessageBox(OffscreenUi::ICON_WARNING, "Duplicate Bookmark", - "The bookmark name you entered already exists in your list.", - QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); - duplicateBookmarkMessage->setProperty("informativeText", "Would you like to overwrite it?"); - - auto result = offscreenUi->waitForMessageBoxResult(duplicateBookmarkMessage); - if (result != QMessageBox::Yes) { - return; - } - removeLocationFromMenu(menubar, bookmarkName); - } - - addLocationToMenu(menubar, bookmarkName, bookmarkAddress); - insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. - - enableMenuItems(true); -} - -void Bookmarks::setHomeLocation() { - Menu* menubar = Menu::getInstance(); - QString bookmarkName = HOME_BOOKMARK; - auto addressManager = DependencyManager::get(); - QString bookmarkAddress = addressManager->currentAddress().toString(); - - addLocationToMenu(menubar, bookmarkName, bookmarkAddress); - insert(bookmarkName, bookmarkAddress); // Overwrites any item with the same bookmarkName. - - enableMenuItems(true); -} - -void Bookmarks::teleportToBookmark() { - QAction* action = qobject_cast(sender()); - QString address = action->data().toString(); - DependencyManager::get()->handleLookupString(address); -} - -void Bookmarks::deleteBookmark() { - - QStringList bookmarkList; - QList menuItems = _bookmarksMenu->actions(); - for (int i = 0; i < menuItems.count(); i += 1) { - bookmarkList.append(menuItems[i]->text()); - } - - bool ok = false; - auto bookmarkName = OffscreenUi::getItem(OffscreenUi::ICON_PLACEMARK, "Delete Bookmark", "Select the bookmark to delete", bookmarkList, 0, false, &ok); - if (!ok) { - return; - } - - bookmarkName = bookmarkName.trimmed(); - if (bookmarkName.length() == 0) { - return; - } - - removeLocationFromMenu(Menu::getInstance(), bookmarkName); - remove(bookmarkName); - - if (_bookmarksMenu->actions().count() == 0) { - enableMenuItems(false); - } -} - void Bookmarks::enableMenuItems(bool enabled) { if (_bookmarksMenu) { _bookmarksMenu->setEnabled(enabled); @@ -210,17 +185,6 @@ void Bookmarks::enableMenuItems(bool enabled) { } } -void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { - QAction* teleportAction = _bookmarksMenu->newAction(); - teleportAction->setData(address); - connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); - - menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, - name, 0, QAction::NoRole); -} - -void Bookmarks::removeLocationFromMenu(Menu* menubar, QString& name) { +void Bookmarks::removeBookmarkFromMenu(Menu* menubar, const QString& name) { menubar->removeAction(_bookmarksMenu, name); } - - diff --git a/interface/src/Bookmarks.h b/interface/src/Bookmarks.h index fd9598b7db..6c322197cc 100644 --- a/interface/src/Bookmarks.h +++ b/interface/src/Bookmarks.h @@ -27,37 +27,35 @@ class Bookmarks: public QObject { public: Bookmarks(); - void setupMenus(Menu* menubar, MenuWrapper* menu); - + virtual void setupMenus(Menu* menubar, MenuWrapper* menu); QString addressForBookmark(const QString& name) const; - static const QString HOME_BOOKMARK; +protected: + virtual void addBookmarkToFile(const QString& bookmarkName, const QString& bookmarkAddress); + virtual void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) = 0; + void enableMenuItems(bool enabled); + void readFromFile(); + void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name. + void sortActions(Menu* menubar, MenuWrapper* menu); + int getMenuItemLocation(QList actions, const QString& name) const; -private slots: - void bookmarkLocation(); - void setHomeLocation(); - void teleportToBookmark(); - void deleteBookmark(); - -private: - QVariantMap _bookmarks; // { name: address, ... } - + QVariantMap _bookmarks; // { name: url, ... } QPointer _bookmarksMenu; QPointer _deleteBookmarksAction; - - const QString BOOKMARKS_FILENAME = "bookmarks.json"; QString _bookmarksFilename; - - void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name. + bool _isMenuSorted; + +protected slots: + void deleteBookmark(); + +private: void remove(const QString& name); bool contains(const QString& name) const; + static bool sortOrder(QAction* a, QAction* b); - void readFromFile(); void persistToFile(); - void enableMenuItems(bool enabled); - void addLocationToMenu(Menu* menubar, QString& name, QString& address); - void removeLocationFromMenu(Menu* menubar, QString& name); + void removeBookmarkFromMenu(Menu* menubar, const QString& name); }; -#endif // hifi_Bookmarks_h \ No newline at end of file +#endif // hifi_Bookmarks_h diff --git a/interface/src/LocationBookmarks.cpp b/interface/src/LocationBookmarks.cpp new file mode 100644 index 0000000000..b79adcf1b7 --- /dev/null +++ b/interface/src/LocationBookmarks.cpp @@ -0,0 +1,88 @@ +// +// LocationBookmarks.cpp +// interface/src +// +// Created by Triplelexx on 23/03/17. +// Copyright 2017 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 +#include +#include +#include + +#include +#include +#include + +#include "MainWindow.h" +#include "Menu.h" + +#include "LocationBookmarks.h" +#include + +const QString LocationBookmarks::HOME_BOOKMARK = "Home"; + +LocationBookmarks::LocationBookmarks() { + _bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + LOCATIONBOOKMARKS_FILENAME; + readFromFile(); +} + +void LocationBookmarks::setupMenus(Menu* menubar, MenuWrapper* menu) { + // Add menus/actions + auto bookmarkAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::BookmarkLocation); + QObject::connect(bookmarkAction, SIGNAL(triggered()), this, SLOT(addBookmark()), Qt::QueuedConnection); + auto setHomeAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::SetHomeLocation); + QObject::connect(setHomeAction, SIGNAL(triggered()), this, SLOT(setHomeLocation()), Qt::QueuedConnection); + _bookmarksMenu = menu->addMenu(MenuOption::LocationBookmarks); + _deleteBookmarksAction = menubar->addActionToQMenuAndActionHash(menu, MenuOption::DeleteBookmark); + QObject::connect(_deleteBookmarksAction, SIGNAL(triggered()), this, SLOT(deleteBookmark()), Qt::QueuedConnection); + + Bookmarks::setupMenus(menubar, menu); + Bookmarks::sortActions(menubar, _bookmarksMenu); +} + +void LocationBookmarks::setHomeLocation() { + auto addressManager = DependencyManager::get(); + QString bookmarkAddress = addressManager->currentAddress().toString(); + Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress); +} + +void LocationBookmarks::teleportToBookmark() { + QAction* action = qobject_cast(sender()); + QString address = action->data().toString(); + DependencyManager::get()->handleLookupString(address); +} + +void LocationBookmarks::addBookmark() { + bool ok = false; + auto bookmarkName = OffscreenUi::getText(OffscreenUi::ICON_PLACEMARK, "Bookmark Location", "Name", QString(), &ok); + if (!ok) { + return; + } + + bookmarkName = bookmarkName.trimmed().replace(QRegExp("(\r\n|[\r\n\t\v ])+"), " "); + if (bookmarkName.length() == 0) { + return; + } + + auto addressManager = DependencyManager::get(); + QString bookmarkAddress = addressManager->currentAddress().toString(); + Bookmarks::addBookmarkToFile(bookmarkName, bookmarkAddress); +} + +void LocationBookmarks::addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) { + QAction* teleportAction = _bookmarksMenu->newAction(); + teleportAction->setData(address); + connect(teleportAction, SIGNAL(triggered()), this, SLOT(teleportToBookmark())); + if (!_isMenuSorted) { + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole); + } else { + // TODO: this is aggressive but other alternatives have proved less fruitful so far. + menubar->addActionToQMenuAndActionHash(_bookmarksMenu, teleportAction, name, 0, QAction::NoRole); + Bookmarks::sortActions(menubar, _bookmarksMenu); + } +} diff --git a/interface/src/LocationBookmarks.h b/interface/src/LocationBookmarks.h new file mode 100644 index 0000000000..1324e96574 --- /dev/null +++ b/interface/src/LocationBookmarks.h @@ -0,0 +1,42 @@ +// +// LocationBookmarks.h +// interface/src +// +// Created by Triplelexx on 23/03/17. +// Copyright 2017 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 +// + +#ifndef hifi_LocationBookmarks_h +#define hifi_LocationBookmarks_h + +#include +#include "Bookmarks.h" + +class LocationBookmarks : public Bookmarks, public Dependency { + Q_OBJECT + SINGLETON_DEPENDENCY + +public: + LocationBookmarks(); + + void setupMenus(Menu* menubar, MenuWrapper* menu) override; + static const QString HOME_BOOKMARK; + +public slots: + void addBookmark(); + +protected: + void addBookmarkToMenu(Menu* menubar, const QString& name, const QString& address) override; + +private: + const QString LOCATIONBOOKMARKS_FILENAME = "bookmarks.json"; + +private slots: + void setHomeLocation(); + void teleportToBookmark(); +}; + +#endif // hifi_LocationBookmarks_h diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 2451f42bca..c99178d8cc 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -32,6 +32,7 @@ #include "assets/ATPAssetMigrator.h" #include "audio/AudioScope.h" #include "avatar/AvatarManager.h" +#include "AvatarBookmarks.h" #include "devices/DdeFaceTracker.h" #include "devices/Faceshift.h" #include "MainWindow.h" @@ -40,6 +41,7 @@ #include "ui/DialogsManager.h" #include "ui/StandAloneJSConsole.h" #include "InterfaceLogging.h" +#include "LocationBookmarks.h" #if defined(Q_OS_MAC) || defined(Q_OS_WIN) #include "SpeechRecognizer.h" @@ -194,6 +196,9 @@ Menu::Menu() { 0, // QML Qt::Key_Apostrophe, qApp, SLOT(resetSensors())); + // Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here. + auto avatarBookmarks = DependencyManager::get(); + avatarBookmarks->setupMenus(this, avatarMenu); // Display menu ---------------------------------- // FIXME - this is not yet matching Alan's spec because it doesn't have @@ -256,8 +261,9 @@ Menu::Menu() { addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L, dialogsManager.data(), SLOT(showAddressBar())); - // Navigate > Bookmark related menus -- Note: the Bookmark class adds its own submenus here. - qApp->getBookmarks()->setupMenus(this, navigateMenu); + // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here. + auto locationBookmarks = DependencyManager::get(); + locationBookmarks->setupMenus(this, navigateMenu); // Navigate > Copy Address [advanced] auto addressManager = DependencyManager::get(); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 14b2f4aeaa..b6f70f5339 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -47,10 +47,11 @@ namespace MenuOption { const QString AudioTools = "Show Level Meter"; const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AvatarReceiveStats = "Show Receive Stats"; + const QString AvatarBookmarks = "Avatar Bookmarks"; const QString Back = "Back"; const QString BinaryEyelidControl = "Binary Eyelid Control"; + const QString BookmarkAvatar = "Bookmark Avatar"; const QString BookmarkLocation = "Bookmark Location"; - const QString Bookmarks = "Bookmarks"; const QString CalibrateCamera = "Calibrate Camera"; const QString CameraEntityMode = "Entity Mode"; const QString CenterPlayerInView = "Center Player In View"; @@ -78,6 +79,7 @@ namespace MenuOption { const QString DeadlockInterface = "Deadlock Interface"; const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DefaultSkybox = "Default Skybox"; + const QString DeleteAvatarBookmark = "Delete Avatar Bookmark..."; const QString DeleteBookmark = "Delete Bookmark..."; const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; @@ -89,6 +91,7 @@ namespace MenuOption { const QString DisplayModelElementChildProxies = "Display Model Element Children"; const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayDebugTimingDetails = "Display Timing Details"; + const QString LocationBookmarks = "Bookmarks"; const QString DontDoPrecisionPicking = "Don't Do Precision Picking"; const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString EchoLocalAudio = "Echo Local Audio"; diff --git a/interface/src/ui/AddressBarDialog.cpp b/interface/src/ui/AddressBarDialog.cpp index ac566d68c7..8aaaac1a57 100644 --- a/interface/src/ui/AddressBarDialog.cpp +++ b/interface/src/ui/AddressBarDialog.cpp @@ -17,6 +17,7 @@ #include "DependencyManager.h" #include "AddressManager.h" #include "DialogsManager.h" +#include "LocationBookmarks.h" HIFI_QML_DEF(AddressBarDialog) @@ -52,7 +53,8 @@ void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions) void AddressBarDialog::loadHome() { qDebug() << "Called LoadHome"; - QString homeLocation = qApp->getBookmarks()->addressForBookmark(Bookmarks::HOME_BOOKMARK); + auto locationBookmarks = DependencyManager::get(); + QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK); const QString DEFAULT_HOME_LOCATION = "localhost"; if (homeLocation == "") { homeLocation = DEFAULT_HOME_LOCATION;