Merge branch 'master' of github.com:highfidelity/hifi into input-recorder

This commit is contained in:
Dante Ruiz 2017-04-18 21:53:37 +01:00
commit f8ec49eb96
23 changed files with 777 additions and 370 deletions

View file

@ -542,7 +542,7 @@ ScrollingWindow {
Item { Item {
height: parent.height height: parent.height
width: parent.width width: parent.width
HifiControls.Button { HifiControls.QueuedButton {
id: uploadButton id: uploadButton
anchors.right: parent.right anchors.right: parent.right
@ -552,22 +552,7 @@ ScrollingWindow {
height: 30 height: 30
width: 155 width: 155
onClicked: uploadClickedTimer.running = true onClickedQueued: uploadClicked()
// 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();
}
} }
Item { Item {

View file

@ -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
}

View file

@ -0,0 +1,53 @@
//
// TabletWebButton.qml
//
// Created by Dante Ruiz on 2017/4/13
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import "../styles-uit"
Rectangle {
property alias text: label.text
property alias pixelSize: label.font.pixelSize;
property bool selected: false
property bool hovered: false
property bool enabled: false
property int spacing: 2
property var action: function () {}
property string enabledColor: hifi.colors.blueHighlight
property string disabledColor: hifi.colors.blueHighlight
property string highlightColor: hifi.colors.blueHighlight;
width: label.width + 64
height: 32
color: hifi.colors.white
HifiConstants { id: hifi }
RalewaySemiBold {
id: label;
color: enabledColor
font.pixelSize: 15;
anchors {
horizontalCenter: parent.horizontalCenter;
verticalCenter: parent.verticalCenter;
}
}
Rectangle {
id: indicator
width: parent.width
height: selected ? 3 : 1
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
color: hifi.colors.blueHighlight
visible: parent.selected || hovered
}
}

View file

@ -2,97 +2,81 @@ import QtQuick 2.5
import QtQuick.Controls 1.4 import QtQuick.Controls 1.4
import QtWebEngine 1.2 import QtWebEngine 1.2
import QtWebChannel 1.0 import QtWebChannel 1.0
import HFTabletWebEngineProfile 1.0
import "../controls-uit" as HiFiControls import "../controls-uit" as HiFiControls
import "../styles" as HifiStyles import "../styles" as HifiStyles
import "../styles-uit" import "../styles-uit"
import HFWebEngineProfile 1.0
import HFTabletWebEngineProfile 1.0
import "../" import "../"
import "."
Item { Item {
id: web id: web
HifiConstants { id: hifi }
width: parent.width width: parent.width
height: parent.height height: parent.height
property var parentStackItem: null property var parentStackItem: null
property int headerHeight: 38 property int headerHeight: 70
property string url property string url
property string address: url //for compatibility property alias address: displayUrl.text //for compatibility
property string scriptURL property string scriptURL
property alias eventBridge: eventBridgeWrapper.eventBridge property alias eventBridge: eventBridgeWrapper.eventBridge
property bool keyboardEnabled: HMD.active property bool keyboardEnabled: HMD.active
property bool keyboardRaised: false property bool keyboardRaised: false
property bool punctuationMode: false property bool punctuationMode: false
property bool isDesktop: false property bool isDesktop: false
property WebEngineView view: loader.currentView
property int currentPage: -1 // used as a model for repeater property int currentPage: -1 // used as a model for repeater
property alias pagesModel: pagesModel property alias pagesModel: pagesModel
Row { Rectangle {
id: buttons id: buttons
HifiConstants { id: hifi } width: parent.width
HifiStyles.HifiConstants { id: hifistyles } height: parent.headerHeight
height: headerHeight color: hifi.colors.white
spacing: 4
anchors.top: parent.top
anchors.topMargin: 8
anchors.left: parent.left
anchors.leftMargin: 8
HiFiGlyphs {
id: back;
enabled: currentPage >= 0
text: hifi.glyphs.backward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: goBack() }
}
HiFiGlyphs {
id: forward;
enabled: currentPage < pagesModel.count - 1
text: hifi.glyphs.forward
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: goForward() }
}
HiFiGlyphs {
id: reload;
enabled: view != null;
text: (view !== null && view.loading) ? hifi.glyphs.close : hifi.glyphs.reload
color: enabled ? hifistyles.colors.text : hifistyles.colors.disabledText
size: 48
MouseArea { anchors.fill: parent; onClicked: reloadPage(); }
}
}
TextField { Row {
id: addressBar id: nav
height: 30 anchors {
anchors.right: parent.right top: parent.top
anchors.rightMargin: 8 topMargin: 10
anchors.left: buttons.right horizontalCenter: parent.horizontalCenter
anchors.leftMargin: 0 }
anchors.verticalCenter: buttons.verticalCenter spacing: 120
focus: true
text: address TabletWebButton {
Component.onCompleted: ScriptDiscoveryService.scriptsModelFilter.filterRegExp = new RegExp("^.*$", "i") id: back
enabledColor: hifi.colors.baseGray
enabled: false
text: "BACK"
Keys.onPressed: { MouseArea {
switch (event.key) { anchors.fill: parent
case Qt.Key_Enter: onClicked: goBack()
case Qt.Key_Return: hoverEnabled: true
event.accepted = true;
if (text.indexOf("http") != 0) { }
text = "http://" + text; }
}
//root.hidePermissionsBar();
web.keyboardRaised = false;
gotoPage(text);
break;
TabletWebButton {
id: close
enabledColor: hifi.colors.darkGray
text: "CLOSE"
MouseArea {
anchors.fill: parent
onClicked: closeWebEngine()
}
}
}
RalewaySemiBold {
id: displayUrl
color: hifi.colors.baseGray
font.pixelSize: 12
anchors {
top: nav.bottom
horizontalCenter: parent.horizontalCenter;
} }
} }
} }
@ -100,15 +84,30 @@ Item {
ListModel { ListModel {
id: pagesModel id: pagesModel
onCountChanged: { onCountChanged: {
currentPage = count - 1 currentPage = count - 1;
if (currentPage > 0) {
back.enabledColor = hifi.colors.darkGray;
} else {
back.enabledColor = hifi.colors.baseGray;
}
} }
} }
function goBack() { function goBack() {
if (currentPage > 0) { if (webview.canGoBack) {
currentPage--; pagesModel.remove(currentPage);
} else if (parentStackItem) { webview.goBack();
} else if (currentPage > 0) {
pagesModel.remove(currentPage);
}
}
function closeWebEngine() {
if (parentStackItem) {
parentStackItem.pop(); parentStackItem.pop();
} else {
web.visible = false;
} }
} }
@ -130,18 +129,20 @@ Item {
function urlAppend(url) { function urlAppend(url) {
var lurl = decodeURIComponent(url) var lurl = decodeURIComponent(url)
if (lurl[lurl.length - 1] !== "/") if (lurl[lurl.length - 1] !== "/") {
lurl = lurl + "/" lurl = lurl + "/"
if (currentPage === -1 || pagesModel.get(currentPage).webUrl !== lurl) { }
pagesModel.append({webUrl: lurl}) if (currentPage === -1 || (pagesModel.get(currentPage).webUrl !== lurl && !timer.running)) {
timer.start();
pagesModel.append({webUrl: lurl});
} }
} }
onCurrentPageChanged: { onCurrentPageChanged: {
if (currentPage >= 0 && currentPage < pagesModel.count && loader.item !== null) { if (currentPage >= 0 && currentPage < pagesModel.count) {
loader.item.url = pagesModel.get(currentPage).webUrl webview.url = pagesModel.get(currentPage).webUrl;
web.url = loader.item.url web.url = webview.url;
web.address = loader.item.url web.address = webview.url;
} }
} }
@ -155,45 +156,110 @@ Item {
property var eventBridge; property var eventBridge;
} }
Loader { Timer {
id: loader id: timer
interval: 100
running: false
repeat: false
onTriggered: timer.stop();
}
property WebEngineView currentView: null
WebEngineView {
id: webview
objectName: "webEngineView"
x: 0
y: 0
width: parent.width width: parent.width
height: parent.height - web.headerHeight height: keyboardEnabled && keyboardRaised ? parent.height - keyboard.height - web.headerHeight : parent.height - web.headerHeight
asynchronous: true
anchors.top: buttons.bottom anchors.top: buttons.bottom
active: false profile: HFTabletWebEngineProfile {
source: "../TabletBrowser.qml" id: webviewTabletProfile
onStatusChanged: { storageName: "qmlTabletWebEngine"
if (loader.status === Loader.Ready) { }
currentView = item.webView
item.webView.userScriptUrl = web.scriptURL property string userScriptUrl: ""
if (currentPage >= 0) {
//we got something to load already // creates a global EventBridge object.
item.url = pagesModel.get(currentPage).webUrl WebEngineScript {
web.address = loader.item.url id: createGlobalEventBridge
sourceCode: eventBridgeJavaScriptToInject
injectionPoint: WebEngineScript.DocumentCreation
worldId: WebEngineScript.MainWorld
}
// detects when to raise and lower virtual keyboard
WebEngineScript {
id: raiseAndLowerKeyboard
injectionPoint: WebEngineScript.Deferred
sourceUrl: resourceDirectoryUrl + "/html/raiseAndLowerKeyboard.js"
worldId: WebEngineScript.MainWorld
}
// User script.
WebEngineScript {
id: userScript
sourceUrl: webview.userScriptUrl
injectionPoint: WebEngineScript.DocumentReady // DOM ready but page load may not be finished.
worldId: WebEngineScript.MainWorld
}
userScripts: [ createGlobalEventBridge, raiseAndLowerKeyboard, userScript ]
property string newUrl: ""
webChannel.registeredObjects: [eventBridgeWrapper]
Component.onCompleted: {
// Ensure the JS from the web-engine makes it to our logging
webview.javaScriptConsoleMessage.connect(function(level, message, lineNumber, sourceID) {
console.log("Web Entity JS message: " + sourceID + " " + lineNumber + " " + message);
});
webview.profile.httpUserAgent = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
web.address = url;
}
onFeaturePermissionRequested: {
grantFeaturePermission(securityOrigin, feature, true);
}
onLoadingChanged: {
keyboardRaised = false;
punctuationMode = false;
keyboard.resetShiftMode(false);
// Required to support clicking on "hifi://" links
if (WebEngineView.LoadStartedStatus == loadRequest.status) {
urlAppend(loadRequest.url.toString())
var url = loadRequest.url.toString();
if (urlHandler.canHandleUrl(url)) {
if (urlHandler.handleUrl(url)) {
root.stop();
}
} }
} }
} }
}
onNewViewRequested: {
request.openIn(webview);
}
}
Component.onCompleted: { Component.onCompleted: {
web.isDesktop = (typeof desktop !== "undefined"); web.isDesktop = (typeof desktop !== "undefined");
address = url; address = url;
loader.active = true
} }
Keys.onPressed: { Keys.onPressed: {
switch(event.key) { switch(event.key) {
case Qt.Key_L: case Qt.Key_L:
if (event.modifiers == Qt.ControlModifier) { if (event.modifiers == Qt.ControlModifier) {
event.accepted = true event.accepted = true
addressBar.selectAll() }
addressBar.forceActiveFocus() break;
}
break;
} }
} }
} }

View file

@ -16,7 +16,6 @@ import "../../hifi/tablet/tabletWindows/preferences"
Preference { Preference {
id: root id: root
property alias text: dataTextField.text property alias text: dataTextField.text
property alias buttonText: button.text
property alias placeholderText: dataTextField.placeholderText property alias placeholderText: dataTextField.placeholderText
property var browser; property var browser;
height: control.height + hifi.dimensions.controlInterlineHeight height: control.height + hifi.dimensions.controlInterlineHeight
@ -58,28 +57,46 @@ Preference {
right: parent.right right: parent.right
bottom: parent.bottom bottom: parent.bottom
} }
height: Math.max(dataTextField.controlHeight, button.height) height: dataTextField.controlHeight + bookmarkAvatarButton.height + hifi.dimensions.contentSpacing.y
TextField { TextField {
id: dataTextField id: dataTextField
label: root.label
placeholderText: root.placeholderText placeholderText: root.placeholderText
text: preference.value text: preference.value
label: root.label colorScheme: dataTextField.acceptableInput ? hifi.colorSchemes.dark : hifi.colorSchemes.light
validator: RegExpValidator {
regExp: /.*\.(?:fst).*\?*/ig
}
anchors { anchors {
left: parent.left left: parent.left
right: button.left right: parent.right
rightMargin: hifi.dimensions.contentSpacing.x bottom: bookmarkAvatarButton.top
bottom: parent.bottom 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 { Button {
id: button id: browseAvatarsButton
text: "Browse" text: "Browse Avatars"
width: 140
anchors { anchors {
right: parent.right left: dataTextField.acceptableInput ? bookmarkAvatarButton.right : parent.left
verticalCenter: dataTextField.verticalCenter bottom: parent.bottom
leftMargin: dataTextField.acceptableInput ? hifi.dimensions.contentSpacing.x : 0
} }
onClicked: { onClicked: {
if (typeof desktop !== "undefined") { if (typeof desktop !== "undefined") {
@ -103,5 +120,6 @@ Preference {
eventBridge: tabletRoot.eventBridge eventBridge: tabletRoot.eventBridge
} }
} }
} }
} }

View file

@ -219,41 +219,18 @@ ScrollingWindow {
Row { Row {
spacing: hifi.dimensions.contentSpacing.x spacing: hifi.dimensions.contentSpacing.x
HifiControls.Button { HifiControls.QueuedButton {
text: "from URL" text: "from URL"
color: hifi.buttons.black color: hifi.buttons.black
height: 26 height: 26
onClicked: fromUrlTimer.running = true onClickedQueued: ApplicationInterface.loadScriptURLDialog()
// 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();
}
} }
HifiControls.Button { HifiControls.QueuedButton {
text: "from Disk" text: "from Disk"
color: hifi.buttons.black color: hifi.buttons.black
height: 26 height: 26
onClicked: fromDiskTimer.running = true onClickedQueued: ApplicationInterface.loadDialog()
Timer {
id: fromDiskTimer
interval: 5
repeat: false
running: false
onTriggered: ApplicationInterface.loadDialog();
}
} }
HifiControls.Button { HifiControls.Button {

View file

@ -37,7 +37,7 @@ StackView {
property var tablet: null; property var tablet: null;
Component { id: tabletStoryCard; TabletStoryCard {} } Component { id: tabletWebView; TabletWebView {} }
Component.onCompleted: { Component.onCompleted: {
fillDestinations(); fillDestinations();
updateLocationText(false); updateLocationText(false);
@ -62,9 +62,9 @@ StackView {
} }
function goCard(targetString) { function goCard(targetString) {
if (0 !== targetString.indexOf('hifi://')) { if (0 !== targetString.indexOf('hifi://')) {
var card = tabletStoryCard.createObject(); var card = tabletWebView.createObject();
card.setUrl(addressBarDialog.metaverseServerUrl + targetString); card.url = addressBarDialog.metaverseServerUrl + targetString;
card.eventBridge = root.eventBridge; card.parentStackItem = root;
root.push(card); root.push(card);
return; return;
} }

View file

@ -1,46 +0,0 @@
//
// TabletAddressDialog.qml
//
// Created by Dante Ruiz on 2017/04/24
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
import Hifi 1.0
import QtQuick 2.4
import QtGraphicalEffects 1.0
import "../../controls"
import "../../styles"
import "../../windows"
import "../"
import "../toolbars"
import "../../styles-uit" as HifiStyles
import "../../controls-uit" as HifiControlsUit
import "../../controls" as HifiControls
Rectangle {
id: cardRoot
HifiStyles.HifiConstants { id: hifi }
width: parent.width
height: parent.height
property string address: ""
property alias eventBridge: webview.eventBridge
function setUrl(url) {
cardRoot.address = url;
webview.url = url;
}
HifiControls.TabletWebView {
id: webview
parentStackItem: root
anchors {
top: parent.top
right: parent.right
left: parent.left
bottom: parent.bottom
}
}
}

View file

@ -59,6 +59,7 @@
#include <AssetUpload.h> #include <AssetUpload.h>
#include <AutoUpdater.h> #include <AutoUpdater.h>
#include <AudioInjectorManager.h> #include <AudioInjectorManager.h>
#include <AvatarBookmarks.h>
#include <CursorManager.h> #include <CursorManager.h>
#include <DebugDraw.h> #include <DebugDraw.h>
#include <DeferredLightingEffect.h> #include <DeferredLightingEffect.h>
@ -82,6 +83,7 @@
#include <controllers/StateController.h> #include <controllers/StateController.h>
#include <UserActivityLoggerScriptingInterface.h> #include <UserActivityLoggerScriptingInterface.h>
#include <LogHandler.h> #include <LogHandler.h>
#include "LocationBookmarks.h"
#include <MainWindow.h> #include <MainWindow.h>
#include <MappingRequest.h> #include <MappingRequest.h>
#include <MessagesClient.h> #include <MessagesClient.h>
@ -531,6 +533,8 @@ bool setupEssentials(int& argc, char** argv) {
DependencyManager::set<EntityScriptServerLogClient>(); DependencyManager::set<EntityScriptServerLogClient>();
DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>(); DependencyManager::set<LimitlessVoiceRecognitionScriptingInterface>();
DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats()); DependencyManager::set<OctreeStatsProvider>(nullptr, qApp->getOcteeSceneStats());
DependencyManager::set<AvatarBookmarks>();
DependencyManager::set<LocationBookmarks>();
return previousSessionCrashed; return previousSessionCrashed;
} }
@ -704,8 +708,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
usleep(USECS_PER_MSEC * 50); // 20hz usleep(USECS_PER_MSEC * 50); // 20hz
} }
_bookmarks = new Bookmarks(); // Before setting up the menu
// start the nodeThread so its event loop is running // start the nodeThread so its event loop is running
QThread* nodeThread = new QThread(this); QThread* nodeThread = new QThread(this);
nodeThread->setObjectName("NodeList Thread"); nodeThread->setObjectName("NodeList Thread");
@ -2010,6 +2012,8 @@ void Application::initializeUi() {
rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance()); rootContext->setContextProperty("Settings", SettingsScriptingInterface::getInstance());
rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data()); rootContext->setContextProperty("ScriptDiscoveryService", DependencyManager::get<ScriptEngines>().data());
rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance()); rootContext->setContextProperty("AudioDevice", AudioDeviceScriptingInterface::getInstance());
rootContext->setContextProperty("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
rootContext->setContextProperty("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
// Caches // Caches
rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data()); rootContext->setContextProperty("AnimationCache", DependencyManager::get<AnimationCache>().data());
@ -5501,6 +5505,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri
scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance()); scriptEngine->registerGlobalObject("AudioDevice", AudioDeviceScriptingInterface::getInstance());
scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data()); scriptEngine->registerGlobalObject("AudioStats", DependencyManager::get<AudioClient>()->getStats().data());
scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data()); scriptEngine->registerGlobalObject("AudioScope", DependencyManager::get<AudioScope>().data());
scriptEngine->registerGlobalObject("AvatarBookmarks", DependencyManager::get<AvatarBookmarks>().data());
scriptEngine->registerGlobalObject("LocationBookmarks", DependencyManager::get<LocationBookmarks>().data());
// Caches // Caches
scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data()); scriptEngine->registerGlobalObject("AnimationCache", DependencyManager::get<AnimationCache>().data());
@ -6366,7 +6372,6 @@ void Application::loadLODToolsDialog() {
} else { } else {
tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml"); tablet->pushOntoStack("../../hifi/dialogs/TabletLODTools.qml");
} }
} }
@ -6416,6 +6421,11 @@ void Application::toggleEntityScriptServerLogDialog() {
} }
} }
void Application::loadAddAvatarBookmarkDialog() const {
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->addBookmark();
}
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) { void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio) {
postLambdaEvent([notify, includeAnimated, aspectRatio, this] { postLambdaEvent([notify, includeAnimated, aspectRatio, this] {
QMediaPlayer* player = new QMediaPlayer(); QMediaPlayer* player = new QMediaPlayer();

View file

@ -52,7 +52,6 @@
#include "avatar/MyAvatar.h" #include "avatar/MyAvatar.h"
#include "BandwidthRecorder.h" #include "BandwidthRecorder.h"
#include "Bookmarks.h"
#include "Camera.h" #include "Camera.h"
#include "ConnectionMonitor.h" #include "ConnectionMonitor.h"
#include "gpu/Context.h" #include "gpu/Context.h"
@ -261,7 +260,6 @@ public:
glm::mat4 getEyeProjection(int eye) const; glm::mat4 getEyeProjection(int eye) const;
QRect getDesirableApplicationGeometry() const; QRect getDesirableApplicationGeometry() const;
Bookmarks* getBookmarks() const { return _bookmarks; }
virtual bool canAcceptURL(const QString& url) const override; virtual bool canAcceptURL(const QString& url) const override;
virtual bool acceptURL(const QString& url, bool defaultUpload = false) override; virtual bool acceptURL(const QString& url, bool defaultUpload = false) override;
@ -330,6 +328,7 @@ public slots:
void toggleEntityScriptServerLogDialog(); void toggleEntityScriptServerLogDialog();
void toggleRunningScriptsWidget() const; void toggleRunningScriptsWidget() const;
Q_INVOKABLE void showAssetServerWidget(QString filePath = ""); Q_INVOKABLE void showAssetServerWidget(QString filePath = "");
Q_INVOKABLE void loadAddAvatarBookmarkDialog() const;
void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const; void showDialog(const QString& desktopURL, const QString& tabletURL, const QString& name) const;
@ -599,8 +598,6 @@ private:
bool _aboutToQuit; bool _aboutToQuit;
Bookmarks* _bookmarks;
bool _notifiedPacketVersionMismatchThisDomain; bool _notifiedPacketVersionMismatchThisDomain;
QThread _settingsThread; QThread _settingsThread;

View file

@ -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 <QAction>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <QQmlContext>
#include <Application.h>
#include <OffscreenUi.h>
#include <avatar/AvatarManager.h>
#include "MainWindow.h"
#include "Menu.h"
#include "AvatarBookmarks.h"
#include <QtQuick/QQuickWindow>
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<QAction*>(sender());
const QString& address = action->data().toString();
auto myAvatar = DependencyManager::get<AvatarManager>()->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<AvatarManager>()->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);
}
}

View file

@ -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 <DependencyManager.h>
#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

View file

@ -11,14 +11,10 @@
#include <QAction> #include <QAction>
#include <QDebug> #include <QDebug>
#include <QJsonObject>
#include <QFile>
#include <QInputDialog> #include <QInputDialog>
#include <QJsonDocument>
#include <QMessageBox> #include <QMessageBox>
#include <QStandardPaths> #include <QStandardPaths>
#include <AddressManager.h>
#include <Application.h> #include <Application.h>
#include <OffscreenUi.h> #include <OffscreenUi.h>
@ -27,15 +23,69 @@
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "Bookmarks.h" #include "Bookmarks.h"
#include <QtQuick/QQuickWindow>
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() { void Bookmarks::deleteBookmark() {
_bookmarksFilename = QStandardPaths::writableLocation(QStandardPaths::DataLocation) + "/" + BOOKMARKS_FILENAME; QStringList bookmarkList;
readFromFile(); QList<QAction*> 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<OffscreenUi>();
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) { void Bookmarks::insert(const QString& name, const QString& address) {
@ -64,6 +114,33 @@ bool Bookmarks::contains(const QString& name) const {
return _bookmarks.contains(name); 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<QAction*> 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<QAction*> 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 { QString Bookmarks::addressForBookmark(const QString& name) const {
return _bookmarks.value(name).toString(); return _bookmarks.value(name).toString();
} }
@ -99,108 +176,6 @@ void Bookmarks::persistToFile() {
saveFile.write(data); 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<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Menu* menubar = Menu::getInstance();
if (contains(bookmarkName)) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
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<AddressManager>();
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<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
void Bookmarks::deleteBookmark() {
QStringList bookmarkList;
QList<QAction*> 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) { void Bookmarks::enableMenuItems(bool enabled) {
if (_bookmarksMenu) { if (_bookmarksMenu) {
_bookmarksMenu->setEnabled(enabled); _bookmarksMenu->setEnabled(enabled);
@ -210,17 +185,6 @@ void Bookmarks::enableMenuItems(bool enabled) {
} }
} }
void Bookmarks::addLocationToMenu(Menu* menubar, QString& name, QString& address) { void Bookmarks::removeBookmarkFromMenu(Menu* menubar, const QString& name) {
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) {
menubar->removeAction(_bookmarksMenu, name); menubar->removeAction(_bookmarksMenu, name);
} }

View file

@ -27,37 +27,35 @@ class Bookmarks: public QObject {
public: public:
Bookmarks(); Bookmarks();
void setupMenus(Menu* menubar, MenuWrapper* menu); virtual void setupMenus(Menu* menubar, MenuWrapper* menu);
QString addressForBookmark(const QString& name) const; 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<QAction*> actions, const QString& name) const;
private slots: QVariantMap _bookmarks; // { name: url, ... }
void bookmarkLocation();
void setHomeLocation();
void teleportToBookmark();
void deleteBookmark();
private:
QVariantMap _bookmarks; // { name: address, ... }
QPointer<MenuWrapper> _bookmarksMenu; QPointer<MenuWrapper> _bookmarksMenu;
QPointer<QAction> _deleteBookmarksAction; QPointer<QAction> _deleteBookmarksAction;
const QString BOOKMARKS_FILENAME = "bookmarks.json";
QString _bookmarksFilename; QString _bookmarksFilename;
bool _isMenuSorted;
void insert(const QString& name, const QString& address); // Overwrites any existing entry with same name.
protected slots:
void deleteBookmark();
private:
void remove(const QString& name); void remove(const QString& name);
bool contains(const QString& name) const; bool contains(const QString& name) const;
static bool sortOrder(QAction* a, QAction* b);
void readFromFile();
void persistToFile(); void persistToFile();
void enableMenuItems(bool enabled); void removeBookmarkFromMenu(Menu* menubar, const QString& name);
void addLocationToMenu(Menu* menubar, QString& name, QString& address);
void removeLocationFromMenu(Menu* menubar, QString& name);
}; };
#endif // hifi_Bookmarks_h #endif // hifi_Bookmarks_h

View file

@ -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 <QAction>
#include <QInputDialog>
#include <QMessageBox>
#include <QStandardPaths>
#include <AddressManager.h>
#include <Application.h>
#include <OffscreenUi.h>
#include "MainWindow.h"
#include "Menu.h"
#include "LocationBookmarks.h"
#include <QtQuick/QQuickWindow>
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<AddressManager>();
QString bookmarkAddress = addressManager->currentAddress().toString();
Bookmarks::addBookmarkToFile(HOME_BOOKMARK, bookmarkAddress);
}
void LocationBookmarks::teleportToBookmark() {
QAction* action = qobject_cast<QAction*>(sender());
QString address = action->data().toString();
DependencyManager::get<AddressManager>()->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<AddressManager>();
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);
}
}

View file

@ -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 <DependencyManager.h>
#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

View file

@ -32,6 +32,7 @@
#include "assets/ATPAssetMigrator.h" #include "assets/ATPAssetMigrator.h"
#include "audio/AudioScope.h" #include "audio/AudioScope.h"
#include "avatar/AvatarManager.h" #include "avatar/AvatarManager.h"
#include "AvatarBookmarks.h"
#include "devices/DdeFaceTracker.h" #include "devices/DdeFaceTracker.h"
#include "devices/Faceshift.h" #include "devices/Faceshift.h"
#include "MainWindow.h" #include "MainWindow.h"
@ -40,6 +41,7 @@
#include "ui/DialogsManager.h" #include "ui/DialogsManager.h"
#include "ui/StandAloneJSConsole.h" #include "ui/StandAloneJSConsole.h"
#include "InterfaceLogging.h" #include "InterfaceLogging.h"
#include "LocationBookmarks.h"
#if defined(Q_OS_MAC) || defined(Q_OS_WIN) #if defined(Q_OS_MAC) || defined(Q_OS_WIN)
#include "SpeechRecognizer.h" #include "SpeechRecognizer.h"
@ -194,6 +196,9 @@ Menu::Menu() {
0, // QML Qt::Key_Apostrophe, 0, // QML Qt::Key_Apostrophe,
qApp, SLOT(resetSensors())); qApp, SLOT(resetSensors()));
// Avatar > AvatarBookmarks related menus -- Note: the AvatarBookmarks class adds its own submenus here.
auto avatarBookmarks = DependencyManager::get<AvatarBookmarks>();
avatarBookmarks->setupMenus(this, avatarMenu);
// Display menu ---------------------------------- // Display menu ----------------------------------
// FIXME - this is not yet matching Alan's spec because it doesn't have // 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, addActionToQMenuAndActionHash(navigateMenu, MenuOption::AddressBar, Qt::CTRL | Qt::Key_L,
dialogsManager.data(), SLOT(showAddressBar())); dialogsManager.data(), SLOT(showAddressBar()));
// Navigate > Bookmark related menus -- Note: the Bookmark class adds its own submenus here. // Navigate > LocationBookmarks related menus -- Note: the LocationBookmarks class adds its own submenus here.
qApp->getBookmarks()->setupMenus(this, navigateMenu); auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
locationBookmarks->setupMenus(this, navigateMenu);
// Navigate > Copy Address [advanced] // Navigate > Copy Address [advanced]
auto addressManager = DependencyManager::get<AddressManager>(); auto addressManager = DependencyManager::get<AddressManager>();

View file

@ -47,10 +47,11 @@ namespace MenuOption {
const QString AudioTools = "Show Level Meter"; const QString AudioTools = "Show Level Meter";
const QString AutoMuteAudio = "Auto Mute Microphone"; const QString AutoMuteAudio = "Auto Mute Microphone";
const QString AvatarReceiveStats = "Show Receive Stats"; const QString AvatarReceiveStats = "Show Receive Stats";
const QString AvatarBookmarks = "Avatar Bookmarks";
const QString Back = "Back"; const QString Back = "Back";
const QString BinaryEyelidControl = "Binary Eyelid Control"; const QString BinaryEyelidControl = "Binary Eyelid Control";
const QString BookmarkAvatar = "Bookmark Avatar";
const QString BookmarkLocation = "Bookmark Location"; const QString BookmarkLocation = "Bookmark Location";
const QString Bookmarks = "Bookmarks";
const QString CalibrateCamera = "Calibrate Camera"; const QString CalibrateCamera = "Calibrate Camera";
const QString CameraEntityMode = "Entity Mode"; const QString CameraEntityMode = "Entity Mode";
const QString CenterPlayerInView = "Center Player In View"; const QString CenterPlayerInView = "Center Player In View";
@ -78,6 +79,7 @@ namespace MenuOption {
const QString DeadlockInterface = "Deadlock Interface"; const QString DeadlockInterface = "Deadlock Interface";
const QString DecreaseAvatarSize = "Decrease Avatar Size"; const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DefaultSkybox = "Default Skybox"; const QString DefaultSkybox = "Default Skybox";
const QString DeleteAvatarBookmark = "Delete Avatar Bookmark...";
const QString DeleteBookmark = "Delete Bookmark..."; const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger"; const QString DisableActivityLogger = "Disable Activity Logger";
const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment"; const QString DisableEyelidAdjustment = "Disable Eyelid Adjustment";
@ -89,6 +91,7 @@ namespace MenuOption {
const QString DisplayModelElementChildProxies = "Display Model Element Children"; const QString DisplayModelElementChildProxies = "Display Model Element Children";
const QString DisplayModelElementProxy = "Display Model Element Bounds"; const QString DisplayModelElementProxy = "Display Model Element Bounds";
const QString DisplayDebugTimingDetails = "Display Timing Details"; const QString DisplayDebugTimingDetails = "Display Timing Details";
const QString LocationBookmarks = "Bookmarks";
const QString DontDoPrecisionPicking = "Don't Do Precision Picking"; const QString DontDoPrecisionPicking = "Don't Do Precision Picking";
const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene"; const QString DontRenderEntitiesAsScene = "Don't Render Entities as Scene";
const QString EchoLocalAudio = "Echo Local Audio"; const QString EchoLocalAudio = "Echo Local Audio";

View file

@ -96,6 +96,14 @@ Avatar::Avatar(RigPointer rig) :
_lastOrientation(), _lastOrientation(),
_worldUpDirection(DEFAULT_UP_DIRECTION), _worldUpDirection(DEFAULT_UP_DIRECTION),
_moving(false), _moving(false),
_smoothPositionTime(SMOOTH_TIME_POSITION),
_smoothPositionTimer(std::numeric_limits<float>::max()),
_smoothOrientationTime(SMOOTH_TIME_ORIENTATION),
_smoothOrientationTimer(std::numeric_limits<float>::max()),
_smoothPositionInitial(),
_smoothPositionTarget(),
_smoothOrientationInitial(),
_smoothOrientationTarget(),
_initialized(false), _initialized(false),
_voiceSphereID(GeometryCache::UNKNOWN_ID) _voiceSphereID(GeometryCache::UNKNOWN_ID)
{ {
@ -349,6 +357,33 @@ void Avatar::simulate(float deltaTime, bool inView) {
_simulationInViewRate.increment(); _simulationInViewRate.increment();
} }
if (!isMyAvatar()) {
if (_smoothPositionTimer < _smoothPositionTime) {
// Smooth the remote avatar movement.
_smoothPositionTimer += deltaTime;
if (_smoothPositionTimer < _smoothPositionTime) {
AvatarData::setPosition(
lerp(_smoothPositionInitial,
_smoothPositionTarget,
easeInOutQuad(glm::clamp(_smoothPositionTimer / _smoothPositionTime, 0.0f, 1.0f)))
);
updateAttitude();
}
}
if (_smoothOrientationTimer < _smoothOrientationTime) {
// Smooth the remote avatar movement.
_smoothOrientationTimer += deltaTime;
if (_smoothOrientationTimer < _smoothOrientationTime) {
AvatarData::setOrientation(
slerp(_smoothOrientationInitial,
_smoothOrientationTarget,
easeInOutQuad(glm::clamp(_smoothOrientationTimer / _smoothOrientationTime, 0.0f, 1.0f)))
);
updateAttitude();
}
}
}
PerformanceTimer perfTimer("simulate"); PerformanceTimer perfTimer("simulate");
{ {
@ -1361,13 +1396,31 @@ glm::quat Avatar::getUncachedRightPalmRotation() const {
} }
void Avatar::setPosition(const glm::vec3& position) { void Avatar::setPosition(const glm::vec3& position) {
AvatarData::setPosition(position); if (isMyAvatar()) {
updateAttitude(); // This is the local avatar, no need to handle any position smoothing.
AvatarData::setPosition(position);
updateAttitude();
return;
}
// Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position.
_smoothPositionInitial = getPosition();
_smoothPositionTarget = position;
_smoothPositionTimer = 0.0f;
} }
void Avatar::setOrientation(const glm::quat& orientation) { void Avatar::setOrientation(const glm::quat& orientation) {
AvatarData::setOrientation(orientation); if (isMyAvatar()) {
updateAttitude(); // This is the local avatar, no need to handle any position smoothing.
AvatarData::setOrientation(orientation);
updateAttitude();
return;
}
// Whether or not there is an existing smoothing going on, just reset the smoothing timer and set the starting position as the avatar's current position, then smooth to the new position.
_smoothOrientationInitial = getOrientation();
_smoothOrientationTarget = orientation;
_smoothOrientationTimer = 0.0f;
} }
void Avatar::updatePalms() { void Avatar::updatePalms() {

View file

@ -230,6 +230,16 @@ public:
bool hasNewJointData() const { return _hasNewJointData; } bool hasNewJointData() const { return _hasNewJointData; }
inline float easeInOutQuad(float lerpValue) {
assert(!((lerpValue < 0.0f) || (lerpValue > 1.0f)));
if (lerpValue < 0.5f) {
return (2.0f * lerpValue * lerpValue);
}
return (lerpValue*(4.0f - 2.0f * lerpValue) - 1.0f);
}
public slots: public slots:
// FIXME - these should be migrated to use Pose data instead // FIXME - these should be migrated to use Pose data instead
@ -244,6 +254,9 @@ public slots:
protected: protected:
friend class AvatarManager; friend class AvatarManager;
const float SMOOTH_TIME_POSITION = 0.125f;
const float SMOOTH_TIME_ORIENTATION = 0.075f;
virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send. virtual const QString& getSessionDisplayNameForTransport() const override { return _empty; } // Save a tiny bit of bandwidth. Mixer won't look at what we send.
QString _empty{}; QString _empty{};
virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter! virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) override { _sessionDisplayName = sessionDisplayName; } // don't use no-op setter!
@ -313,6 +326,15 @@ protected:
RateCounter<> _skeletonModelSimulationRate; RateCounter<> _skeletonModelSimulationRate;
RateCounter<> _jointDataSimulationRate; RateCounter<> _jointDataSimulationRate;
// Smoothing data for blending from one position/orientation to another on remote agents.
float _smoothPositionTime;
float _smoothPositionTimer;
float _smoothOrientationTime;
float _smoothOrientationTimer;
glm::vec3 _smoothPositionInitial;
glm::vec3 _smoothPositionTarget;
glm::quat _smoothOrientationInitial;
glm::quat _smoothOrientationTarget;
private: private:
class AvatarEntityDataHash { class AvatarEntityDataHash {

View file

@ -34,9 +34,13 @@ void HFTabletWebEngineRequestInterceptor::interceptRequest(QWebEngineUrlRequestI
QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token; QString bearerTokenString = "Bearer " + accountManager->getAccountInfo().getAccessToken().token;
info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit()); info.setHttpHeader(OAUTH_AUTHORIZATION_HEADER.toLocal8Bit(), bearerTokenString.toLocal8Bit());
} }
}
static const QString USER_AGENT = "User-Agent"; static const QString USER_AGENT = "User-Agent";
QString tokenString = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36"; QString tokenString = "Chrome/48.0 (HighFidelityInterface)";
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit()); info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
} else {
static const QString USER_AGENT = "User-Agent";
QString tokenString = "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Mobile Safari/537.36";
info.setHttpHeader(USER_AGENT.toLocal8Bit(), tokenString.toLocal8Bit());
}
} }

View file

@ -17,6 +17,7 @@
#include "DependencyManager.h" #include "DependencyManager.h"
#include "AddressManager.h" #include "AddressManager.h"
#include "DialogsManager.h" #include "DialogsManager.h"
#include "LocationBookmarks.h"
HIFI_QML_DEF(AddressBarDialog) HIFI_QML_DEF(AddressBarDialog)
@ -52,7 +53,8 @@ void AddressBarDialog::loadAddress(const QString& address, bool fromSuggestions)
void AddressBarDialog::loadHome() { void AddressBarDialog::loadHome() {
qDebug() << "Called LoadHome"; qDebug() << "Called LoadHome";
QString homeLocation = qApp->getBookmarks()->addressForBookmark(Bookmarks::HOME_BOOKMARK); auto locationBookmarks = DependencyManager::get<LocationBookmarks>();
QString homeLocation = locationBookmarks->addressForBookmark(LocationBookmarks::HOME_BOOKMARK);
const QString DEFAULT_HOME_LOCATION = "localhost"; const QString DEFAULT_HOME_LOCATION = "localhost";
if (homeLocation == "") { if (homeLocation == "") {
homeLocation = DEFAULT_HOME_LOCATION; homeLocation = DEFAULT_HOME_LOCATION;

View file

@ -400,6 +400,7 @@ function getProfilePicture(username, callback) { // callback(url) if successfull
var matched = !error && html.match(/img class="users-img" src="([^"]*)"/); var matched = !error && html.match(/img class="users-img" src="([^"]*)"/);
if (!matched) { if (!matched) {
print('Error: Unable to get profile picture for', username, error); print('Error: Unable to get profile picture for', username, error);
callback('');
return; return;
} }
callback(matched[1]); callback(matched[1]);