From 080774387d1e0c455bc6a8701ab4ee77f4bcc025 Mon Sep 17 00:00:00 2001 From: elmanytas Date: Sun, 9 Sep 2018 08:22:58 +0200 Subject: [PATCH 001/307] Added steps to run interface in Ubuntu 18.04 . --- BUILD_LINUX.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/BUILD_LINUX.md b/BUILD_LINUX.md index 1ee3d2b7c8..ea8171cf57 100644 --- a/BUILD_LINUX.md +++ b/BUILD_LINUX.md @@ -86,19 +86,73 @@ In a server, it does not make sense to compile interface ### Running the software +#### Domain server + Running domain server: ```bash ./domain-server/domain-server ``` +#### Assignment clients + Running assignment client: ```bash ./assignment-client/assignment-client -n 6 ``` +#### Interface + Running interface: ```bash ./interface/interface ``` Go to localhost in the running interface. + +##### Ubuntu 18.04 only + +In Ubuntu 18.04 there is a problem related with NVidia driver library version. + +It can be workarounded following these steps: + +Uninstall incompatible nvtt libraries: +```bash +sudo apt-get remove libnvtt2 libnvtt-dev +``` + +Install libssl1.0-dev: +```bash +sudo apt-get -y install libssl1.0-dev +``` + +Clone castano nvidia-texture-tools: +``` +git clone https://github.com/castano/nvidia-texture-tools +cd nvidia-texture-tools/ +``` + +Make these changes in repo: +* In file **VERSION** set `2.2.1` +* In file **configure**: + * set `build="release"` + * set `-DNVTT_SHARED=1` + +Configure, build and install: +``` +./configure +make +sudo make install +``` + +Link compiled files: +``` +sudo ln -s /usr/local/lib/libnvcore.so /usr/lib/libnvcore.so +sudo ln -s /usr/local/lib/libnvimage.so /usr/lib/libnvimage.so +sudo ln -s /usr/local/lib/libnvmath.so /usr/lib/libnvmath.so +sudo ln -s /usr/local/lib/libnvtt.so /usr/lib/libnvtt.so +``` + +After running this steps you can run interface: +``` +interface/interface +``` From 72851acd8b2117482d12f79297dc5ed87b829c16 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 1 Oct 2018 16:44:01 -0700 Subject: [PATCH 002/307] adding spacer/close button pos --- .../resources/qml/windows/ModalFrame.qml | 47 ++++++++++++------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/windows/ModalFrame.qml b/interface/resources/qml/windows/ModalFrame.qml index cb23ccd5ad..bfa029dfd6 100644 --- a/interface/resources/qml/windows/ModalFrame.qml +++ b/interface/resources/qml/windows/ModalFrame.qml @@ -85,6 +85,35 @@ Frame { y: -hifi.dimensions.modalDialogTitleHeight anchors.right: parent.right } + // spacer for closeButton + Rectangle { + id: closeButtonSpacer + width: parent.width - closeButton.width - 50 + height: 20 + color: "transparent" + anchors.left: title.right + y: -hifi.dimensions.modalDialogTitleHeight + visible: window.closeButtonVisible + } + + GlyphButton { + id: closeButton + visible: window.closeButtonVisible + width: 30 + y: -hifi.dimensions.modalDialogTitleHeight - 20 + anchors { + // top: parent.top + left: closeButtonSpacer.right + // topMargin: 10 + // rightMargin: 10 + } + glyph: hifi.glyphs.close + size: 23 + onClicked: { + window.clickedCloseButton = true; + window.destroy(); + } + } } Rectangle { @@ -96,23 +125,5 @@ Frame { } - GlyphButton { - id: closeButton - visible: window.closeButtonVisible - width: 30 - y: -hifi.dimensions.modalDialogTitleHeight - anchors { - top: parent.top - right: parent.right - topMargin: 10 - rightMargin: 10 - } - glyph: hifi.glyphs.close - size: 23 - onClicked: { - window.clickedCloseButton = true; - window.destroy(); - } - } } } From e254324dbd34651227a43b8db4ed5d78c1a04fc2 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 1 Oct 2018 16:47:10 -0700 Subject: [PATCH 003/307] Merging more files --- interface/resources/qml/LoginDialog.qml | 15 ++++++++++----- .../resources/qml/LoginDialog/LinkAccountBody.qml | 5 ++++- interface/resources/qml/windows/ModalFrame.qml | 2 -- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 336858502d..bda05d42be 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -11,15 +11,15 @@ import Hifi 1.0 import QtQuick 2.4 -import "controls-uit" -import "styles-uit" -import "windows" +import "controls-uit" as HifiControlsUit +import "styles-uit" as HifiStylesUit +import "windows" as Windows import "LoginDialog" -ModalWindow { +Windows.ModalWindow { id: root - HifiConstants { id: hifi } + HifiStylesUit.HifiConstants { id: hifi } objectName: "LoginDialog" implicitWidth: 520 implicitHeight: 320 @@ -85,4 +85,9 @@ ModalWindow { break } } + + /* Component.onCompleted: { + root.implicitWidth = Window.innerWidth; + root.implicitHeight = Window.innerHeight; + } */ } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 48cf124127..4236c48baf 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -334,7 +334,10 @@ Item { root.keyboardEnabled = HMD.active; root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) } - d.resize(); + // d.resize(); + + print(root.width); + print(root.height); if (failAfterSignUp) { mainTextContainer.text = "Account created successfully." diff --git a/interface/resources/qml/windows/ModalFrame.qml b/interface/resources/qml/windows/ModalFrame.qml index bfa029dfd6..f076ea9580 100644 --- a/interface/resources/qml/windows/ModalFrame.qml +++ b/interface/resources/qml/windows/ModalFrame.qml @@ -123,7 +123,5 @@ Frame { color: hifi.colors.lightGray } } - - } } From ccebf3edef562d85bcf98c3d40a02c9fa2649cdd Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 8 Oct 2018 17:18:40 -0700 Subject: [PATCH 004/307] omitting keyboard focus hack --- interface/src/Application.cpp | 23 +++++++---------------- libraries/ui/src/OffscreenUi.cpp | 2 +- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index aa2b382c58..16ab667233 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2328,23 +2328,14 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); #else - static int CHECK_LOGIN_TIMER = 3000; - QTimer* checkLoginTimer = new QTimer(this); - checkLoginTimer->setInterval(CHECK_LOGIN_TIMER); - checkLoginTimer->setSingleShot(true); - connect(checkLoginTimer, &QTimer::timeout, this, []() { - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - }); + if (!accountManager->isLoggedIn()) { + Setting::Handle{"loginDialogPoppedUp", false}.set(true); + dialogsManager->showLoginDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + } Setting::Handle{"loginDialogPoppedUp", false}.set(false); - checkLoginTimer->start(); #endif } diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 0aad297587..2cbd6517b0 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -673,7 +673,7 @@ void OffscreenUi::createDesktop(const QUrl& url) { menuInitializer(_vrMenu); } - new KeyboardFocusHack(); + // new KeyboardFocusHack(); connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); emit desktopReady(); }); From 60bbcc7fc31b654ed8a70d7553dc0552c56765ac Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 9 Oct 2018 15:20:16 -0700 Subject: [PATCH 005/307] adding initialization of qml when keyboard ready --- interface/src/Application.cpp | 34 ++++++++++++++++++++++---------- interface/src/Application.h | 1 + libraries/ui/src/OffscreenUi.cpp | 25 +++++++++++++---------- libraries/ui/src/OffscreenUi.h | 1 + 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 16ab667233..f53e329e10 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1775,11 +1775,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); + connect(offscreenUi.data(), &OffscreenUi::keyboardFocusActive, [this]() { + initializeQml(); + }); + // Make sure we don't time out during slow operations at startup updateHeartbeat(); QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ - // This needs to run on the settings thread, so we need to pass the `settingsTimer` as the + // This needs to run on the settings thread, so we need to pass the `settingsTimer` as the // receiver object, otherwise it will run on the application thread and trigger a warning // about trying to kill the timer on the main thread. connect(qApp, &Application::beforeAboutToQuit, settingsTimer, [this, settingsTimer]{ @@ -2327,15 +2331,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&AndroidHelper::instance(), &AndroidHelper::enterBackground, this, &Application::enterBackground); connect(&AndroidHelper::instance(), &AndroidHelper::enterForeground, this, &Application::enterForeground); AndroidHelper::instance().notifyLoadComplete(); -#else - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - Setting::Handle{"loginDialogPoppedUp", false}.set(false); #endif } @@ -2883,6 +2878,25 @@ void Application::initializeRenderEngine() { extern void setupPreferences(); static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false); +void Application::initializeQml() { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "initializeQml"); + return; + } +#if !defined(Q_OS_ANDROID) + auto accountManager = DependencyManager::get(); + auto dialogsManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + Setting::Handle{"loginDialogPoppedUp", false}.set(true); + dialogsManager->showLoginDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + } + Setting::Handle{"loginDialogPoppedUp", false}.set(false); +#endif +} + void Application::initializeUi() { AddressBarDialog::registerType(); ErrorDialog::registerType(); diff --git a/interface/src/Application.h b/interface/src/Application.h index 75260b910f..8b061dda07 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -149,6 +149,7 @@ public: void initializeGL(); void initializeDisplayPlugins(); void initializeRenderEngine(); + void initializeQml(); void initializeUi(); void updateSecondaryCameraViewFrustum(); diff --git a/libraries/ui/src/OffscreenUi.cpp b/libraries/ui/src/OffscreenUi.cpp index 2cbd6517b0..dd4031b68d 100644 --- a/libraries/ui/src/OffscreenUi.cpp +++ b/libraries/ui/src/OffscreenUi.cpp @@ -637,24 +637,28 @@ public: KeyboardFocusHack() { Q_ASSERT(_mainWindow); QTimer::singleShot(200, [=] { - _hackWindow = new QWindow(); - _hackWindow->setFlags(Qt::FramelessWindowHint); - _hackWindow->setGeometry(_mainWindow->x(), _mainWindow->y(), 10, 10); - _hackWindow->show(); - _hackWindow->requestActivate(); + _window = new QWindow(); + _window->setFlags(Qt::FramelessWindowHint); + _window->setGeometry(_mainWindow->x(), _mainWindow->y(), 10, 10); + _window->show(); + _window->requestActivate(); QTimer::singleShot(200, [=] { - _hackWindow->hide(); - _hackWindow->deleteLater(); - _hackWindow = nullptr; + _window->hide(); + _window->deleteLater(); + _window = nullptr; _mainWindow->requestActivate(); + emit keyboardFocusActive(); this->deleteLater(); }); }); } +signals: + void keyboardFocusActive(); + private: QWindow* const _mainWindow { MainWindow::findMainWindow() }; - QWindow* _hackWindow { nullptr }; + QWindow* _window { nullptr }; }; void OffscreenUi::createDesktop(const QUrl& url) { @@ -673,9 +677,10 @@ void OffscreenUi::createDesktop(const QUrl& url) { menuInitializer(_vrMenu); } - // new KeyboardFocusHack(); + auto keyboardFocus = new KeyboardFocusHack(); connect(_desktop, SIGNAL(showDesktop()), this, SIGNAL(showDesktop())); emit desktopReady(); + connect(keyboardFocus, SIGNAL(keyboardFocusActive()), this, SIGNAL(keyboardFocusActive())); }); } diff --git a/libraries/ui/src/OffscreenUi.h b/libraries/ui/src/OffscreenUi.h index fbf7068b1c..46dbdbdf13 100644 --- a/libraries/ui/src/OffscreenUi.h +++ b/libraries/ui/src/OffscreenUi.h @@ -244,6 +244,7 @@ signals: // void assetDialogResponse(QString response); // void inputDialogResponse(QVariant response); void desktopReady(); + void keyboardFocusActive(); public slots: void removeModalDialog(QObject* modal); From 3488763d94f5ebfbbd0c9335e4021b911667e745 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 9 Oct 2018 16:29:56 -0700 Subject: [PATCH 006/307] reverting login dialog stuff --- interface/resources/qml/LoginDialog.qml | 4 ---- interface/resources/qml/LoginDialog/LinkAccountBody.qml | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index bda05d42be..65ac50a234 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -86,8 +86,4 @@ Windows.ModalWindow { } } - /* Component.onCompleted: { - root.implicitWidth = Window.innerWidth; - root.implicitHeight = Window.innerHeight; - } */ } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 4236c48baf..48cf124127 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -334,10 +334,7 @@ Item { root.keyboardEnabled = HMD.active; root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) } - // d.resize(); - - print(root.width); - print(root.height); + d.resize(); if (failAfterSignUp) { mainTextContainer.text = "Account created successfully." From 35eb857ea6660947ac762ad1e40c5240c1c13509 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 9 Oct 2018 16:30:47 -0700 Subject: [PATCH 007/307] adding non-working login screen --- .../qml/hifi/dialogs/LoginScreenDialog.qml | 84 +++++++++++++++++++ interface/src/ui/LoginDialog.cpp | 2 +- interface/src/ui/LoginScreenDialog.cpp | 13 +++ interface/src/ui/LoginScreenDialog.h | 13 +++ 4 files changed, 111 insertions(+), 1 deletion(-) create mode 100644 interface/resources/qml/hifi/dialogs/LoginScreenDialog.qml create mode 100644 interface/src/ui/LoginScreenDialog.cpp create mode 100644 interface/src/ui/LoginScreenDialog.h diff --git a/interface/resources/qml/hifi/dialogs/LoginScreenDialog.qml b/interface/resources/qml/hifi/dialogs/LoginScreenDialog.qml new file mode 100644 index 0000000000..eb4558190b --- /dev/null +++ b/interface/resources/qml/hifi/dialogs/LoginScreenDialog.qml @@ -0,0 +1,84 @@ +// +// LoginScreenDialog.qml +// +// Created by Wayne Chen on 2018/10/9 +// Copyright 2018 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.7 +import QtQuick.Controls 1.4 +import QtQuick.Controls.Styles 1.4 as OriginalStyles +import "qrc:///controls-uit" as HifiControlsUit +import "qrc:///styles-uit" as HifiStylesUit + + +LoginDialog { + id: loginDialog + + Loader { + id: bodyLoader + Item { + + function login() { + flavorText.visible = false + mainTextContainer.visible = false + toggleLoading(true) + loginDialog.login(usernameField.text, passwordField.text) + } + + Connections { + target: loginScreenDialog + onHandleLoginCompleted: { + console.log("Login Succeeded, linking steam account") + var poppedUp = Settings.getValue("loginDialogPoppedUp", false); + if (poppedUp) { + console.log("[ENCOURAGELOGINDIALOG]: logging in") + var data = { + "action": "user logged in" + }; + UserActivityLogger.logAction("encourageLoginDialog", data); + Settings.setValue("loginDialogPoppedUp", false); + } + if (loginDialog.isSteamRunning()) { + loginDialog.linkSteam() + } else { + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + onHandleLoginFailed: { + console.log("Login Failed") + var poppedUp = Settings.getValue("loginDialogPoppedUp", false); + if (poppedUp) { + console.log("[ENCOURAGELOGINDIALOG]: failed logging in") + var data = { + "action": "user failed logging in" + }; + UserActivityLogger.logAction("encourageLoginDialog", data); + Settings.setValue("loginDialogPoppedUp", false); + } + flavorText.visible = true + mainTextContainer.visible = true + toggleLoading(false) + } + onHandleLinkCompleted: { + console.log("Link Succeeded") + + bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + onHandleLinkFailed: { + console.log("Link Failed") + toggleLoading(false) + } + } + + } + } +} diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index ed22492c33..ceea47d969 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -82,7 +82,7 @@ void LoginDialog::toggleAction() { connection = connect(loginAction, &QAction::triggered, accountManager.data(), &AccountManager::logout); } else { // change the menu item to login - loginAction->setText("Login / Sign Up"); + loginAction->setText("Log In / Sign Up"); connection = connect(loginAction, &QAction::triggered, [] { LoginDialog::showWithSelection(); }); } } diff --git a/interface/src/ui/LoginScreenDialog.cpp b/interface/src/ui/LoginScreenDialog.cpp new file mode 100644 index 0000000000..b7e1c875eb --- /dev/null +++ b/interface/src/ui/LoginScreenDialog.cpp @@ -0,0 +1,13 @@ +// +// LoginScreenDialog.cpp +// interface/src/ui +// +// Created by Wayne Chen on 2018/10/9 +// Copyright 2018 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 +// +LoginScreenDialog::LoginScreenDialog() { + +} diff --git a/interface/src/ui/LoginScreenDialog.h b/interface/src/ui/LoginScreenDialog.h new file mode 100644 index 0000000000..ee82c81c44 --- /dev/null +++ b/interface/src/ui/LoginScreenDialog.h @@ -0,0 +1,13 @@ +// +// LoginScreenDialog.h +// interface/src/ui +// +// Created by Wayne Chen on 2018/10/9 +// Copyright 2018 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 +// +class HMDLoginScreenDialog : public QmlWindowClass { + virtual QString qmlSource() const override { return "hifi/dialogs/.qml"; } +}; From 28a950fe9f876ca7334d87ef27f4c1b12964d876 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 15 Oct 2018 16:51:28 -0700 Subject: [PATCH 008/307] staging --- interface/src/Application.cpp | 121 ++++++++++++++++---- interface/src/Application.h | 11 +- interface/src/ConnectionMonitor.cpp | 1 + interface/src/ui/DialogsManager.cpp | 7 +- interface/src/ui/DialogsManager.h | 4 + interface/src/ui/LoginScreenDialog.cpp | 4 +- libraries/networking/src/AddressManager.cpp | 11 +- libraries/networking/src/AddressManager.h | 1 + libraries/networking/src/DomainHandler.cpp | 25 +++- libraries/networking/src/DomainHandler.h | 11 ++ scripts/system/marketplaces/marketplaces.js | 42 +++++++ 11 files changed, 207 insertions(+), 31 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index f53e329e10..e9cc9161ca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1198,6 +1198,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, SIGNAL(domainURLChanged(QUrl)), SLOT(domainURLChanged(QUrl))); connect(&domainHandler, SIGNAL(redirectToErrorDomainURL(QUrl)), SLOT(goToErrorDomainURL(QUrl))); + connect(this, SIGNAL(loginScreenStateChanged(bool)), &domainHandler, SLOT((loginScreenStateChanged(bool)))); connect(&domainHandler, &DomainHandler::domainURLChanged, [](QUrl domainURL){ setCrashAnnotation("domain", domainURL.toString().toStdString()); }); @@ -1212,6 +1213,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &Application::domainConnectionRefused); nodeList->getDomainHandler().setErrorDomainURL(QUrl(REDIRECT_HIFI_ADDRESS)); + nodeList->getDomainHandler().setLoginScreenDomainURL(QUrl(LOGIN_SCREEN_HIFI_ADDRESS)); // We could clear ATP assets only when changing domains, but it's possible that the domain you are connected // to has gone down and switched to a new content set, so when you reconnect the cached ATP assets will no longer be valid. @@ -2277,6 +2279,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, &QCoreApplication::aboutToQuit, this, &Application::addAssetToWorldMessageClose); connect(&domainHandler, &DomainHandler::domainURLChanged, this, &Application::addAssetToWorldMessageClose); connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &Application::addAssetToWorldMessageClose); + connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &Application::addAssetToWorldMessageClose); updateSystemTabletMode(); @@ -2878,22 +2881,31 @@ void Application::initializeRenderEngine() { extern void setupPreferences(); static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, int index, bool active = false); +void Application::showLoginScreen() { + auto accountManager = DependencyManager::get(); + if (!accountManager->isLoggedIn()) { + Setting::Handle{"loginDialogPoppedUp", false}.set(true); +// dialogsManager->showLoginScreenDialog(); + QJsonObject loginData = {}; + loginData["action"] = "login dialog shown"; + UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); + if (qApp->isHMDMode()) { + // create web overlay. + auto nodeList = DependencyManager::get(); + auto loginScreenDomainURL = nodeList->getDomainHandler().getLoginScreenDomainURL(); + goToLoginScreenDomainURL(loginScreenDomainURL); + } + } + Setting::Handle{"loginDialogPoppedUp", false}.set(false); +} + void Application::initializeQml() { if (QThread::currentThread() != thread()) { QMetaObject::invokeMethod(this, "initializeQml"); return; } #if !defined(Q_OS_ANDROID) - auto accountManager = DependencyManager::get(); - auto dialogsManager = DependencyManager::get(); - if (!accountManager->isLoggedIn()) { - Setting::Handle{"loginDialogPoppedUp", false}.set(true); - dialogsManager->showLoginDialog(); - QJsonObject loginData = {}; - loginData["action"] = "login dialog shown"; - UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); - } - Setting::Handle{"loginDialogPoppedUp", false}.set(false); + showLoginScreen(); #endif } @@ -3551,13 +3563,9 @@ void Application::setIsServerlessMode(bool serverlessDomain) { } } -void Application::loadServerlessDomain(QUrl domainURL, bool errorDomain) { +std::map Application::prepareServerlessDomainContents(QUrl domainURL) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL), Q_ARG(bool, errorDomain)); - return; - } - - if (domainURL.isEmpty()) { + QMetaObject::invokeMethod(this, "prepareServerlessDomainContents", Q_ARG(QUrl, domainURL)); return; } @@ -3584,13 +3592,61 @@ void Application::loadServerlessDomain(QUrl domainURL, bool errorDomain) { tmpTree->sendEntities(&_entityEditSender, getEntities()->getTree(), 0, 0, 0); } - std::map namedPaths = tmpTree->getNamedPaths(); - if (errorDomain) { - nodeList->getDomainHandler().loadedErrorDomain(namedPaths); - } else { - nodeList->getDomainHandler().connectedToServerless(namedPaths); + return tmpTree->getNamedPaths(); + +} + +void Application::loadServerlessDomain(QUrl domainURL) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadServerlessDomain", Q_ARG(QUrl, domainURL)); + return; } + if (domainURL.isEmpty()) { + return; + } + + auto namedPaths = prepareServerlessDomainContents(domainURL); + auto nodeList = DependencyManager::get(); + + nodeList->getDomainHandler().connectedToServerless(namedPaths); + + _fullSceneReceivedCounter++; +} + +void Application::loadErrorDomain(QUrl domainURL) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadErrorDomain", Q_ARG(QUrl, domainURL)); + return; + } + + if (domainURL.isEmpty()) { + return; + } + + auto namedPaths = prepareServerlessDomainContents(domainURL); + auto nodeList = DependencyManager::get(); + + nodeList->getDomainHandler().loadedErrorDomain(namedPaths); + + _fullSceneReceivedCounter++; +} + +void Application::loadLoginScreenDomain(QUrl domainURL) { + if (QThread::currentThread() != thread()) { + QMetaObject::invokeMethod(this, "loadLoginScreenDomain", Q_ARG(QUrl, domainURL)); + return; + } + + if (domainURL.isEmpty()) { + return; + } + + auto namedPaths = prepareServerlessDomainContents(domainURL); + auto nodeList = DependencyManager::get(); + + nodeList->getDomainHandler().loadedLoginScreenDomain(namedPaths); + _fullSceneReceivedCounter++; } @@ -6410,6 +6466,7 @@ void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); auto accountManager = DependencyManager::get(); auto isInErrorState = nodeList->getDomainHandler().isInErrorState(); + auto isInLoginScreenState = nodeList->getDomainHandler().isInLoginScreenState(); QString buildVersion = " - " + (BuildInfo::BUILD_TYPE == BuildInfo::BuildType::Stable ? QString("Version") : QString("Build")) @@ -6419,6 +6476,8 @@ void Application::updateWindowTitle() const { QString connectionStatus = isInErrorState ? " (ERROR CONNECTING)" : nodeList->getDomainHandler().isConnected() ? "" : " (NOT CONNECTED)"; + // check for login state - login state needs empty connection status + connectionStatus = isInLoginScreenState ? "" : connectionStatus; QString username = accountManager->getAccountInfo().getUsername(); setCrashAnnotation("username", username.toStdString()); @@ -6427,6 +6486,8 @@ void Application::updateWindowTitle() const { if (isServerlessMode()) { if (isInErrorState) { currentPlaceName = "serverless: " + nodeList->getDomainHandler().getErrorDomainURL().toString(); + } else if (isInLoginScreenState) { + currentPlaceName = "High Fidelity Interface"; } else { currentPlaceName = "serverless: " + DependencyManager::get()->getDomainURL().toString(); } @@ -6497,11 +6558,27 @@ void Application::goToErrorDomainURL(QUrl errorDomainURL) { resetPhysicsReadyInformation(); setIsServerlessMode(errorDomainURL.scheme() != URL_SCHEME_HIFI); if (isServerlessMode()) { - loadServerlessDomain(errorDomainURL, true); + loadErrorDomain(errorDomainURL); } updateWindowTitle(); } +void Application::goToLoginScreenDomainURL(QUrl loginScreenDomainURL) { + // disable physics until we have enough information about our new location to not cause craziness. + resetPhysicsReadyInformation(); + setIsServerlessMode(loginScreenDomainURL.scheme() != URL_SCHEME_HIFI); + + // show avatar as a mesh and show hand controllers. + qApp->getMyAvatar()->setEnableMeshVisible(false); + DependencyManager::get()->requestShowHandControllers(); + // set into login screen state. + emit loginScreenStateChanged(true); + + if (isServerlessMode()) { + loadLoginScreenDomain(loginScreenDomainURL); + } + updateWindowTitle(); +} void Application::resettingDomain() { _notifiedPacketVersionMismatchThisDomain = false; diff --git a/interface/src/Application.h b/interface/src/Application.h index 8b061dda07..38ceebbb8b 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -335,7 +335,7 @@ signals: void uploadRequest(QString path); - void loginDialogPoppedUp(); + void loginScreenStateChanged(bool isInLoginScreenState); public slots: QVector pasteEntities(float x, float y, float z); @@ -345,6 +345,7 @@ public slots: void updateThreadPoolCount() const; void updateSystemTabletMode(); void goToErrorDomainURL(QUrl errorDomainURL); + void goToLoginScreenDomainURL(QUrl loginScreenDomainURL); Q_INVOKABLE void loadDialog(); Q_INVOKABLE void loadScriptURLDialog() const; @@ -357,6 +358,8 @@ public slots: void showDialog(const QUrl& widgetUrl, const QUrl& tabletUrl, const QString& name) const; + void showLoginScreen(); + // FIXME: Move addAssetToWorld* methods to own class? void addAssetToWorldFromURL(QString url); void addAssetToWorldFromURLRequestFinished(); @@ -433,7 +436,11 @@ public slots: void setPreferredCursor(const QString& cursor); void setIsServerlessMode(bool serverlessDomain); - void loadServerlessDomain(QUrl domainURL, bool errorDomain = false); + std::map prepareServerlessDomainContents(QUrl domainURL); + + void loadServerlessDomain(QUrl domainURL); + void loadErrorDomain(QUrl domainURL); + void loadLoginScreenDomain(QUrl domainURL); void setIsInterstitialMode(bool interstitialMode); void updateVerboseLogging(); diff --git a/interface/src/ConnectionMonitor.cpp b/interface/src/ConnectionMonitor.cpp index e86061b090..ff89d9a0d6 100644 --- a/interface/src/ConnectionMonitor.cpp +++ b/interface/src/ConnectionMonitor.cpp @@ -33,6 +33,7 @@ void ConnectionMonitor::init() { connect(&domainHandler, &DomainHandler::connectedToDomain, this, &ConnectionMonitor::stopTimer); connect(&domainHandler, &DomainHandler::domainConnectionRefused, this, &ConnectionMonitor::stopTimer); connect(&domainHandler, &DomainHandler::redirectToErrorDomainURL, this, &ConnectionMonitor::stopTimer); + connect(&domainHandler, &DomainHandler::redirectToLoginScreenDomainURL, this, &ConnectionMonitor::stopTimer); connect(this, &ConnectionMonitor::setRedirectErrorState, &domainHandler, &DomainHandler::setRedirectErrorState); _timer.setSingleShot(true); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index 83601a2797..e2721f4ebb 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -25,6 +25,7 @@ #include "HMDToolsDialog.h" #include "LodToolsDialog.h" #include "LoginDialog.h" +#include "LoginScreenDialog.h" #include "OctreeStatsDialog.h" #include "PreferencesDialog.h" #include "UpdateDialog.h" @@ -117,6 +118,10 @@ void DialogsManager::showLoginDialog() { LoginDialog::showWithSelection(); } +void DialogsManager::showLoginScreenDialog() { + LoginScreenDialog::showWithSelection(); +} + void DialogsManager::showUpdateDialog() { UpdateDialog::show(); } @@ -203,4 +208,4 @@ void DialogsManager::showDomainConnectionDialog() { _domainConnectionDialog->show(); _domainConnectionDialog->raise(); -} +} \ No newline at end of file diff --git a/interface/src/ui/DialogsManager.h b/interface/src/ui/DialogsManager.h index 0633dec573..9b8a051ade 100644 --- a/interface/src/ui/DialogsManager.h +++ b/interface/src/ui/DialogsManager.h @@ -46,8 +46,12 @@ public slots: void hideAddressBar(); void showFeed(); void setDomainConnectionFailureVisibility(bool visible); + // toggles login screen that appears upon `File > Log In/Sign Up`. void toggleLoginDialog(); + // shows login screen that appears upon `File > Log In/Sign Up`. void showLoginDialog(); + // toggles login screen that appears upon application startup. + void showLoginScreenDialog(); void octreeStatsDetails(); void lodTools(); void hmdTools(bool showTools); diff --git a/interface/src/ui/LoginScreenDialog.cpp b/interface/src/ui/LoginScreenDialog.cpp index b7e1c875eb..8ffc332ef1 100644 --- a/interface/src/ui/LoginScreenDialog.cpp +++ b/interface/src/ui/LoginScreenDialog.cpp @@ -8,6 +8,4 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -LoginScreenDialog::LoginScreenDialog() { - -} +#include "LoginScreenDialog.h" diff --git a/libraries/networking/src/AddressManager.cpp b/libraries/networking/src/AddressManager.cpp index f8ab8ceaec..137ce2ad4a 100644 --- a/libraries/networking/src/AddressManager.cpp +++ b/libraries/networking/src/AddressManager.cpp @@ -32,6 +32,7 @@ const QString DEFAULT_HIFI_ADDRESS = "file:///~/serverless/tutorial.json"; const QString REDIRECT_HIFI_ADDRESS = "file:///~/serverless/redirect.json"; +const QString LOGIN_SCREEN_HIFI_ADDRESS = "file:///~/serverless/login.json"; const QString ADDRESS_MANAGER_SETTINGS_GROUP = "AddressManager"; const QString SETTINGS_CURRENT_ADDRESS_KEY = "address"; @@ -162,11 +163,15 @@ void AddressManager::storeCurrentAddress() { // url.scheme() == URL_SCHEME_HTTP || // url.scheme() == URL_SCHEME_HTTPS || bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); + bool isInLoginScreenState = DependencyManager::get()->getDomainHandler().isInLoginScreenState(); + if (isInLoginScreenState) { + qCWarning(networking) << "Ignoring attempt to save current address because in login screen domain:" << url; + } if (isConnected()) { if (isInErrorState) { // save the last address visited before the problem url. currentAddressHandle.set(lastAddress()); - } else { + } else { currentAddressHandle.set(url); } } else { @@ -824,8 +829,10 @@ bool AddressManager::setDomainInfo(const QUrl& domainURL, LookupTrigger trigger) bool emitHostChanged { false }; // Check if domain handler is in error state. always emit host changed if true. bool isInErrorState = DependencyManager::get()->getDomainHandler().isInErrorState(); + // Check if domain handler is in login screen state. always emit host changed if true. + bool isInLoginScreenState = DependencyManager::get()->getDomainHandler().isInLoginScreenState(); - if (domainURL != _domainURL || isInErrorState) { + if (domainURL != _domainURL || isInErrorState || isInLoginScreenState) { addCurrentAddressToHistory(trigger); emitHostChanged = true; } diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h index 5318822cdc..07c8e0f934 100644 --- a/libraries/networking/src/AddressManager.h +++ b/libraries/networking/src/AddressManager.h @@ -24,6 +24,7 @@ extern const QString DEFAULT_HIFI_ADDRESS; extern const QString REDIRECT_HIFI_ADDRESS; +extern const QString LOGIN_SCREEN_HIFI_ADDRESS; const QString SANDBOX_HIFI_ADDRESS = "hifi://localhost"; const QString INDEX_PATH = "/"; diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 615546b410..4140ff1a64 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -62,6 +62,9 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if redirected to the error domain connect(this, &DomainHandler::redirectToErrorDomainURL, &_apiRefreshTimer, &QTimer::stop); + + // stop the refresh timer if redirected to the login screen domain + connect(this, &DomainHandler::redirectToLoginScreenDomainURL, &_apiRefreshTimer, &QTimer::stop); } void DomainHandler::disconnect() { @@ -113,7 +116,7 @@ void DomainHandler::softReset() { QMetaObject::invokeMethod(&_settingsTimer, "stop"); // restart the API refresh timer in case we fail to connect and need to refresh information - if (!_isInErrorState) { + if (!_isInErrorState || !_isInLoginScreenState) { QMetaObject::invokeMethod(&_apiRefreshTimer, "start"); } } @@ -125,6 +128,9 @@ void DomainHandler::hardReset() { _isInErrorState = false; emit redirectErrorStateChanged(_isInErrorState); + _isInLoginScreenState = false; + emit loginScreenStateChanged(_isInLoginScreenState); + qCDebug(networking) << "Hard reset in NodeList DomainHandler."; _pendingDomainID = QUuid(); _iceServerSockAddr = HifiSockAddr(); @@ -363,6 +369,17 @@ void DomainHandler::loadedErrorDomain(std::map namedPaths) { DependencyManager::get()->goToViewpointForPath(viewpoint, QString()); } +void DomainHandler::loadedLoginScreenDomain(std::map namedPaths) { + auto lookup = namedPaths.find("/"); + QString viewpoint; + if (lookup != namedPaths.end()) { + viewpoint = lookup->second; + } else { + viewpoint = DOMAIN_SPAWNING_POINT; + } + DependencyManager::get()->goToViewpointForPath(viewpoint, QString()); +} + void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, int reasonCode, const QString& extraInfo) { _lastDomainConnectionError = reasonCode; if (getInterstitialModeEnabled() && isHardRefusal(reasonCode)) { @@ -376,6 +393,12 @@ void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, } } +void DomainHandler::redirectToLoginScreenDomainURL() { + _isInLoginScreenState = true; + qCDebug(networking) << "Redirecting user to " << _loginScreenDomainURL; + +} + void DomainHandler::requestDomainSettings() { qCDebug(networking) << "Requesting settings from domain server"; diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h index ddd23339df..9bfbe26db0 100644 --- a/libraries/networking/src/DomainHandler.h +++ b/libraries/networking/src/DomainHandler.h @@ -58,6 +58,9 @@ public: int getLastDomainConnectionError() { return _lastDomainConnectionError; } + QUrl getLoginScreenDomainURL(){ return _loginScreenDomainURL; } + void setLoginScreenDomainURL(const QUrl& url); + const QHostAddress& getIP() const { return _sockAddr.getAddress(); } void setIPToLocalhost() { _sockAddr.setAddress(QHostAddress(QHostAddress::LocalHost)); } @@ -93,6 +96,8 @@ public: void loadedErrorDomain(std::map namedPaths); + void loadedLoginScreenDomain(std::map namedPaths); + QString getViewPointFromNamedPath(QString namedPath); bool hasSettings() const { return !_settingsObject.isEmpty(); } @@ -179,6 +184,7 @@ public slots: void setRedirectErrorState(QUrl errorUrl, QString reasonMessage = "", int reason = -1, const QString& extraInfo = ""); bool isInErrorState() { return _isInErrorState; } + bool isInLoginScreenState() { return _isInLoginScreenState; } private slots: void completedHostnameLookup(const QHostInfo& hostInfo); @@ -205,6 +211,9 @@ signals: void redirectToErrorDomainURL(QUrl errorDomainURL); void redirectErrorStateChanged(bool isInErrorState); + void redirectToLoginScreenDomainURL(); + void loginScreenStateChanged(bool isInLoginScreenState); + void limitOfSilentDomainCheckInsReached(); private: @@ -218,6 +227,7 @@ private: Node::LocalID _localID; QUrl _domainURL; QUrl _errorDomainURL; + QUrl _loginScreenDomainURL; HifiSockAddr _sockAddr; QUuid _assignmentUUID; QUuid _connectionToken; @@ -227,6 +237,7 @@ private: NetworkPeer _icePeer; bool _isConnected { false }; bool _isInErrorState { false }; + bool _isInLoginScreenState { false }; QJsonObject _settingsObject; QString _pendingPath; QTimer _settingsTimer; diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index cca535a064..ef5472271e 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -917,6 +917,7 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { Menu.setIsOptionChecked("Disable Preview", true); setTabletVisibleInSecondaryCamera(false); } +<<<<<<< Updated upstream break; case 'maybeEnableHmdPreview': maybeEnableHMDPreview(); @@ -962,6 +963,47 @@ var onQmlMessageReceived = function onQmlMessageReceived(message) { Script.update.connect(updateOverlays); isUpdateOverlaysWired = true; } +======= + } + +<<<<<<< Updated upstream + // Function Name: onTabletScreenChanged() + // + // Description: + // -Called when the TabletScriptingInterface::screenChanged() signal is emitted. The "type" argument can be either the string + // value of "Home", "Web", "Menu", "QML", or "Closed". The "url" argument is only valid for Web and QML. + var onWalletScreen = false; + var onCommerceScreen = false; + function onTabletScreenChanged(type, url) { + onMarketplaceScreen = type === "Web" && url.indexOf(MARKETPLACE_URL) !== -1; + var onWalletScreenNow = url.indexOf(MARKETPLACE_WALLET_QML_PATH) !== -1; + var onCommerceScreenNow = type === "QML" && (url.indexOf(MARKETPLACE_CHECKOUT_QML_PATH) !== -1 || url === MARKETPLACE_PURCHASES_QML_PATH + || url.indexOf(MARKETPLACE_INSPECTIONCERTIFICATE_QML_PATH) !== -1); + + if ((!onWalletScreenNow && onWalletScreen) || (!onCommerceScreenNow && onCommerceScreen)) { // exiting wallet or commerce screen + maybeEnableHMDPreview(); + } +======= + // console.debug(ui.buttonName + " app reports: Tablet screen changed.\nNew screen type: " + type + + // "\nNew screen URL: " + url + "\nCurrent app open status: " + ui.isOpen + "\n"); +}; + +function notificationDataProcessPage(data) { + return data.data.updates; +} +>>>>>>> Stashed changes + + onCommerceScreen = onCommerceScreenNow; + onWalletScreen = onWalletScreenNow; + wireEventBridge(onMarketplaceScreen || onCommerceScreen || onWalletScreen); + + if (url === MARKETPLACE_PURCHASES_QML_PATH) { + sendToQml({ + method: 'updatePurchases', + referrerURL: referrerURL, + filterText: filterText + }); +>>>>>>> Stashed changes } break; case 'disable_ChooseRecipientNearbyMode': From 3bd10716b2ee19a0446e9d9d60c708444e65e48f Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Fri, 19 Oct 2018 16:06:28 -0700 Subject: [PATCH 009/307] wip --- .../resources/images/high-fidelity-banner.svg | 17 + interface/resources/qml/LoginDialog.qml | 10 +- .../qml/LoginDialog/LinkAccountBody.qml | 393 +++++++----------- .../qml/LoginDialog/LoggingInDialog.qml | 9 + .../resources/qml/LoginDialog/SignInBody.qml | 217 +++++++--- .../resources/qml/LoginDialog/SignUpBody.qml | 6 +- .../LoginDialog/images/loader-snake-128.gif | Bin 0 -> 59768 bytes .../LoginDialog/images/loader-snake-64-w.gif | Bin 0 -> 50046 bytes interface/src/Application.cpp | 7 +- interface/src/ui/DialogsManager.cpp | 1 - interface/src/ui/LoginScreenDialog.cpp | 18 + interface/src/ui/LoginScreenDialog.h | 16 +- libraries/networking/src/DomainHandler.cpp | 18 +- 13 files changed, 383 insertions(+), 329 deletions(-) create mode 100644 interface/resources/images/high-fidelity-banner.svg create mode 100644 interface/resources/qml/LoginDialog/LoggingInDialog.qml create mode 100644 interface/resources/qml/LoginDialog/images/loader-snake-128.gif create mode 100644 interface/resources/qml/LoginDialog/images/loader-snake-64-w.gif diff --git a/interface/resources/images/high-fidelity-banner.svg b/interface/resources/images/high-fidelity-banner.svg new file mode 100644 index 0000000000..d5666be0fa --- /dev/null +++ b/interface/resources/images/high-fidelity-banner.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index 65ac50a234..ebf4e9e3a6 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -1,8 +1,8 @@ // // LoginDialog.qml // -// Created by David Rowe on 3 Jun 2015 -// Copyright 2015 High Fidelity, Inc. +// Created by Wayne Chen +// Copyright 2018 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 @@ -23,10 +23,12 @@ Windows.ModalWindow { objectName: "LoginDialog" implicitWidth: 520 implicitHeight: 320 - closeButtonVisible: true destroyOnCloseButton: true destroyOnHidden: true visible: true + frame: Item {} + + anchors.fill: parent readonly property bool isTablet: false @@ -36,8 +38,6 @@ Windows.ModalWindow { property string title: "" property int titleWidth: 0 - keyboardOverride: true // Disable ModalWindow's keyboard. - function tryDestroy() { root.destroy() } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 48cf124127..758e8555cd 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -13,44 +13,36 @@ import QtQuick 2.7 import QtQuick.Controls 1.4 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import "qrc:///qml//controls-uit" as HifiControlsUit +import "qrc:///qml//styles-uit" as HifiStylesUit Item { id: linkAccountBody clip: true height: root.pane.height width: root.pane.width + property bool isTablet: root.isTablet property bool failAfterSignUp: false - function login() { - flavorText.visible = false - mainTextContainer.visible = false - toggleLoading(true) - loginDialog.login(usernameField.text, passwordField.text) - } - property bool keyboardEnabled: false property bool keyboardRaised: false property bool punctuationMode: false + onKeyboardRaisedChanged: d.resize(); QtObject { id: d readonly property int minWidth: 480 - readonly property int maxWidth: 1280 + property int maxWidth: root.isTablet ? 1280 : Window.innerWidth readonly property int minHeight: 120 - readonly property int maxHeight: 720 + property int maxHeight: root.isTablet ? 720 : Window.innerHeight function resize() { - var targetWidth = Math.max(titleWidth, form.contentWidth); - var targetHeight = hifi.dimensions.contentSpacing.y + flavorText.height + mainTextContainer.height + - 4 * hifi.dimensions.contentSpacing.y + form.height; - - if (additionalInformation.visible) { - targetWidth = Math.max(targetWidth, additionalInformation.width); - targetHeight += hifi.dimensions.contentSpacing.y + additionalInformation.height - } + maxWidth = root.isTablet ? 1280 : Window.innerWidth; + maxHeight = root.isTablet ? 720 : Window.innerHeight; + var targetWidth = Math.max(Math.max(titleWidth, topContainer.width), bottomContainer.width); + var targetHeight = hifi.dimensions.contentSpacing.y + topContainer.height + bottomContainer.height + + 4 * hifi.dimensions.contentSpacing.y; var newWidth = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); if(!isNaN(newWidth)) { @@ -71,253 +63,159 @@ Item { } } - BusyIndicator { - id: linkAccountSpinner - - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - running: true - - width: 48 - height: 48 - } - - ShortcutText { - id: flavorText - anchors { - top: parent.top - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - text: qsTr("Sign in to High Fidelity to make friends, get HFC, and buy interesting things on the Marketplace!") - width: parent.width - wrapMode: Text.WordWrap - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - ShortcutText { - id: mainTextContainer - anchors { - top: flavorText.bottom - left: parent.left - margins: 0 - topMargin: 1.5 * hifi.dimensions.contentSpacing.y - } - - visible: false - text: qsTr("Username or password incorrect.") - height: flavorText.height - 20 - wrapMode: Text.WordWrap - color: hifi.colors.redAccent - lineHeight: 1 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Column { - id: form - width: parent.width + Item { + id: topContainer + width: root.pane.width + height: 0.6 * root.pane.height onHeightChanged: d.resize(); onWidthChanged: d.resize(); - anchors { - top: mainTextContainer.bottom - topMargin: 1.5 * hifi.dimensions.contentSpacing.y - } - spacing: 2 * hifi.dimensions.contentSpacing.y - - TextField { - id: usernameField - text: Settings.getValue("wallet/savedUsername", ""); + Item { + id: bannerContainer width: parent.width - focus: true - placeholderText: "Username or Email" - activeFocusOnPress: true - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - ShortcutText { - z: 10 - y: usernameField.height - anchors { - right: usernameField.right - top: usernameField.bottom - topMargin: 4 - } - - text: "Forgot Username?" - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - linkColor: hifi.colors.blueAccent - - onLinkActivated: loginDialog.openUrl(link) + height: banner.height + anchors { + top: parent.top + topMargin: 0.17 * parent.height } - - onFocusChanged: { - root.text = ""; - } - Component.onCompleted: { - var savedUsername = Settings.getValue("wallet/savedUsername", ""); - usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername; + Image { + id: banner + anchors.centerIn: parent + source: "../../images/high-fidelity-banner.svg" + horizontalAlignment: Image.AlignHCenter } } - - TextField { - id: passwordField - width: parent.width - placeholderText: "Password" - activeFocusOnPress: true - echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - ShortcutText { - id: forgotPasswordShortcut - y: passwordField.height - z: 10 - anchors { - right: passwordField.right - top: passwordField.bottom - topMargin: 4 - } - - text: "Forgot Password?" - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - linkColor: hifi.colors.blueAccent - - onLinkActivated: loginDialog.openUrl(link) + Text { + id: flavorText + text: qsTr("BE ANYWHERE, WITH ANYONE RIGHT NOW") + width: 0.48 * parent.width + anchors.centerIn: parent + anchors { + top: bannerContainer.bottom + topMargin: 0.1 * parent.height } - - onFocusChanged: { - root.text = ""; - root.isPassword = true; - } - - Rectangle { - id: showPasswordHitbox - z: 10 - x: passwordField.width - ((passwordField.height) * 31 / 23) - width: parent.width - (parent.width - (parent.height * 31/16)) - height: parent.height - anchors { - right: parent.right - } - color: "transparent" - - Image { - id: showPasswordImage - width: passwordField.height * 16 / 23 - height: passwordField.height * 16 / 23 - anchors { - right: parent.right - rightMargin: 8 - top: parent.top - topMargin: passwordFieldMouseArea.showPassword ? 6 : 8 - bottom: parent.bottom - bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8 - } - source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg" - MouseArea { - id: passwordFieldMouseArea - anchors.fill: parent - acceptedButtons: Qt.LeftButton - property bool showPassword: false - onClicked: { - showPassword = !showPassword; - } - } - } - - } - - Keys.onReturnPressed: linkAccountBody.login() - } - - InfoItem { - id: additionalInformation - - visible: loginDialog.isSteamRunning() - - text: qsTr("Your steam account informations will not be exposed to other users.") wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight lineHeight: 1 + color: "white" + font.family: "Raleway" + font.pixelSize: 55 + font.bold: true lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter } - Row { - id: buttons - spacing: hifi.dimensions.contentSpacing.y*2 - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - anchors.horizontalCenter: parent.horizontalCenter - - CheckBox { - id: autoLogoutCheckbox - checked: !Settings.getValue("wallet/autoLogout", true) - text: "Keep me signed in" - boxSize: 20; - labelFontSize: 15 - color: hifi.colors.black - onCheckedChanged: { - Settings.setValue("wallet/autoLogout", !checked); - if (checked) { - Settings.setValue("wallet/savedUsername", Account.username); - } else { - Settings.setValue("wallet/savedUsername", ""); - } - } - Component.onDestruction: { - Settings.setValue("wallet/autoLogout", !checked); - } + HifiControlsUit.Button { + id: signUpButton + text: qsTr("Sign Up") + width: 0.17 * parent.width + height: 0.068 * parent.height + color: hifi.buttons.blue + fontSize: 30 + anchors { + top: flavorText.bottom + topMargin: 0.1 * parent.height + left: parent.left + leftMargin: (parent.width - signUpButton.width) / 2 } - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr(loginDialog.isSteamRunning() ? "Link Account" : "Log in") - color: hifi.buttons.blue - - onClicked: linkAccountBody.login() + onClicked: { + bodyLoader.setSource("SignUpBody.qml") + if (!linkAccountBody.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } } } + } + Item { + id: bottomContainer + width: root.pane.width + height: 0.4 * root.pane.height + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + anchors.top: topContainer.bottom - Row { - id: leftButton + Rectangle { + height: root.pane.height + width: root.pane.width + opacity: 0.7 + color: "black" + } - anchors.horizontalCenter: parent.horizontalCenter - spacing: hifi.dimensions.contentSpacing.y*2 - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - RalewaySemiBold { - size: hifi.fontSizes.inputLabel - anchors.verticalCenter: parent.verticalCenter - text: qsTr("New to High Fidelity?") + HifiControlsUit.Button { + id: loginButton + width: signUpButton.width + height: signUpButton.height + text: qsTr("Log In") + fontSize: signUpButton.fontSize + // background: Rectangle { + // radius: hifi.buttons.radius + // + // } + anchors { + top: parent.top + topMargin: 0.245 * parent.height + left: parent.left + leftMargin: (parent.width - loginButton.width) / 2 } - Button { - anchors.verticalCenter: parent.verticalCenter + onClicked: { + bodyLoader.setSource("SignInBody.qml") + if (!linkAccountBody.isTablet) { + loginDialog.bodyLoader.item.width = root.pane.width + loginDialog.bodyLoader.item.height = root.pane.height + } + } + } + HifiControlsUit.Button { + id: steamLoginButton + width: signUpButton.width + height: signUpButton.height + text: qsTr("Link Account") + fontSize: signUpButton.fontSize + color: hifi.buttons.black + anchors { + top: loginButton.bottom + topMargin: 0.04 * parent.height + left: parent.left + leftMargin: (parent.width - steamLoginButton.width) / 2 + } - text: qsTr("Sign Up") - visible: !loginDialog.isSteamRunning() + onClicked: { + if (loginDialog.isSteamRunning()) { + loginDialog.linkSteam(); + } + if (!linkAccountBody.isTablet) { + bodyLoader.item.width = root.pane.width + bodyLoader.item.height = root.pane.height + } + } + } + Item { + id: dismissTextContainer + width: dismissText.width + height: dismissText.height + anchors { + bottom: parent.bottom + right: parent.right + margins: 10 + } + Text { + id: dismissText + text: qsTr("No thanks, take me in-world! >") + lineHeight: 1 + color: "white" + font.family: "Raleway" + font.pixelSize: 20 + font.bold: true + lineHeightMode: Text.ProportionalHeight + // horizontalAlignment: Text.AlignHCenter + } + MouseArea { + id: dismissMouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton onClicked: { - bodyLoader.setSource("SignUpBody.qml") - if (!root.isTablet) { - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } + root.tryDestroy(); } } } @@ -336,13 +234,12 @@ Item { } d.resize(); - if (failAfterSignUp) { - mainTextContainer.text = "Account created successfully." - flavorText.visible = true - mainTextContainer.visible = true - } + // if (failAfterSignUp) { + // mainTextContainer.text = "Account created successfully." + // flavorText.visible = true + // mainTextContainer.visible = true + // } - usernameField.forceActiveFocus(); } Connections { diff --git a/interface/resources/qml/LoginDialog/LoggingInDialog.qml b/interface/resources/qml/LoginDialog/LoggingInDialog.qml new file mode 100644 index 0000000000..e00f1178b1 --- /dev/null +++ b/interface/resources/qml/LoginDialog/LoggingInDialog.qml @@ -0,0 +1,9 @@ +// +// LoggingInDialog.qml +// +// Created by Wayne Chen +// Copyright 2018 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 +// diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 9cb1add704..e3b490300b 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls.Styles 1.4 as OriginalStyles -import "../controls-uit" -import "../styles-uit" +import "qrc:///qml//controls-uit" as HifiControlsUit +import "qrc:///qml//styles-uit" as HifiStylesUit Item { id: signInBody @@ -24,87 +24,186 @@ Item { property bool required: false function login() { - console.log("Trying to log in") - loginDialog.loginThroughSteam() + bodyLoader.setSource("LoggingInBody.qml"); + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width; + bodyLoader.item.height = root.pane.height; + } + loginDialog.login(usernameField.text, passwordField.text); } function cancel() { - root.tryDestroy() + bodyLoader.setSource("LinkAccountBody.qml"); + if (!root.isTablet) { + bodyLoader.item.width = root.pane.width; + bodyLoader.item.height = root.pane.height; + } } QtObject { id: d readonly property int minWidth: 480 - readonly property int maxWidth: 1280 + property int maxWidth: root.isTablet ? 1280 : Window.innerWidth readonly property int minHeight: 120 - readonly property int maxHeight: 720 + property int maxHeight: root.isTablet ? 720 : Window.innerHeight function resize() { - var targetWidth = Math.max(titleWidth, mainTextContainer.contentWidth) - var targetHeight = mainTextContainer.height + 3 * hifi.dimensions.contentSpacing.y + buttons.height + maxWidth = root.isTablet ? 1280 : Window.innerWidth; + maxHeight = root.isTablet ? 720 : Window.innerHeight; + var targetWidth = Math.max(titleWidth, form.contentWidth) + var targetHeight = 4 * hifi.dimensions.contentSpacing.y + form.height parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)) parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) } } - - InfoItem { - id: mainTextContainer - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y + + Item { + width: parent.width + height: parent.height + Rectangle { + width: parent.width + height: parent.height + opacity: 0.7 + color: "black" } - - text: required ? qsTr("This domain's owner requires that you sign in:") - : qsTr("Sign in to access your user account:") - wrapMode: Text.WordWrap - color: hifi.colors.baseGrayHighlight - lineHeight: 2 - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter - } - - Row { - id: buttons - anchors { - top: mainTextContainer.bottom - horizontalCenter: parent.horizontalCenter - margins: 0 - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - width: undefined // invalidate so that the image's size sets the width - height: undefined // invalidate so that the image's size sets the height - focus: true - - background: Image { - id: buttonImage - source: "../../images/steam-sign-in.png" + Item { + id: bannerContainer + width: parent.width + height: banner.height + anchors { + top: parent.top + topMargin: 0.17 * parent.height + } + Image { + id: banner + anchors.centerIn: parent + source: "../../images/high-fidelity-banner.svg" + horizontalAlignment: Image.AlignHCenter } - onClicked: signInBody.login() } - Button { - anchors.verticalCenter: parent.verticalCenter + Column { + id: form + width: parent.width + onHeightChanged: d.resize(); onWidthChanged: d.resize(); - text: qsTr("Cancel"); + anchors { + top: bannerContainer.bottom + topMargin: 1.5 * hifi.dimensions.contentSpacing.y + } + spacing: 2 * hifi.dimensions.contentSpacing.y - onClicked: signInBody.cancel() + HifiControlsUit.TextField { + id: usernameField + width: 0.254 * parent.width + text: Settings.getValue("wallet/savedUsername", ""); + focus: true + placeholderText: "Username or Email" + activeFocusOnPress: true + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + onFocusChanged: { + root.text = ""; + } + Component.onCompleted: { + var savedUsername = Settings.getValue("wallet/savedUsername", ""); + usernameField.text = savedUsername === "Unknown user" ? "" : savedUsername; + } + } + HifiControlsUit.TextField { + id: passwordField + width: 0.254 * parent.width + placeholderText: "Password" + activeFocusOnPress: true + echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + onFocusChanged: { + root.text = ""; + root.isPassword = true; + } + + Rectangle { + id: showPasswordHitbox + z: 10 + x: passwordField.width - ((passwordField.height) * 31 / 23) + width: parent.width - (parent.width - (parent.height * 31/16)) + height: parent.height + anchors { + right: parent.right + } + color: "transparent" + + Image { + id: showPasswordImage + width: passwordField.height * 16 / 23 + height: passwordField.height * 16 / 23 + anchors { + right: parent.right + rightMargin: 8 + top: parent.top + topMargin: passwordFieldMouseArea.showPassword ? 6 : 8 + bottom: parent.bottom + bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8 + } + source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg" + MouseArea { + id: passwordFieldMouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + property bool showPassword: false + onClicked: { + showPassword = !showPassword; + } + } + } + + } + + Keys.onReturnPressed: signInBody.login() + } } } - Component.onCompleted: { - root.title = required ? qsTr("Sign In Required") - : qsTr("Sign In") - root.iconText = "" - d.resize(); - } + // Row { + // id: buttons + // anchors { + // top: mainTextContainer.bottom + // horizontalCenter: parent.horizontalCenter + // margins: 0 + // topMargin: 2 * hifi.dimensions.contentSpacing.y + // } + // spacing: hifi.dimensions.contentSpacing.x + // onHeightChanged: d.resize(); onWidthChanged: d.resize(); + // + // Button { + // anchors.verticalCenter: parent.verticalCenter + // + // width: undefined // invalidate so that the image's size sets the width + // height: undefined // invalidate so that the image's size sets the height + // focus: true + // + // background: Image { + // id: buttonImage + // source: "../../images/steam-sign-in.png" + // } + // onClicked: signInBody.login() + // } + // Button { + // anchors.verticalCenter: parent.verticalCenter + // + // text: qsTr("Cancel"); + // + // onClicked: signInBody.cancel() + // } + // } + + // Component.onCompleted: { + // root.title = required ? qsTr("Sign In Required") + // : qsTr("Sign In") + // root.iconText = "" + // d.resize(); + // } Connections { target: loginDialog diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml index bb30696e4c..f2980f22c5 100644 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ b/interface/resources/qml/LoginDialog/SignUpBody.qml @@ -1,7 +1,7 @@ // // SignUpBody.qml // -// Created by Stephen Birarda on 7 Dec 2016 +// Created by Wayne Chen on Oct 18 2018 // Copyright 2016 High Fidelity, Inc. // // Distributed under the Apache License, Version 2.0. @@ -12,8 +12,8 @@ import Hifi 1.0 import QtQuick 2.7 import QtQuick.Controls 1.4 -import "../controls-uit" -import "../styles-uit" +import "qrc:///qml//controls-uit" +import "qrc:///qml//styles-uit" Item { id: signupBody diff --git a/interface/resources/qml/LoginDialog/images/loader-snake-128.gif b/interface/resources/qml/LoginDialog/images/loader-snake-128.gif new file mode 100644 index 0000000000000000000000000000000000000000..8b9e17053b2420e2ea3dcf68e3677d172858a9b5 GIT binary patch literal 59768 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 K@P|MDwfPqs;|oUs literal 0 HcmV?d00001 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 54df0ac3a5..cfe656aabd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2885,9 +2885,11 @@ static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, in void Application::showLoginScreen() { auto accountManager = DependencyManager::get(); + auto dialogsManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { Setting::Handle{"loginDialogPoppedUp", false}.set(true); // dialogsManager->showLoginScreenDialog(); + dialogsManager->showLoginDialog(); QJsonObject loginData = {}; loginData["action"] = "login dialog shown"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); @@ -3566,11 +3568,6 @@ void Application::setIsServerlessMode(bool serverlessDomain) { } std::map Application::prepareServerlessDomainContents(QUrl domainURL) { - if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "prepareServerlessDomainContents", Q_ARG(QUrl, domainURL)); - return; - } - QUuid serverlessSessionID = QUuid::createUuid(); getMyAvatar()->setSessionUUID(serverlessSessionID); auto nodeList = DependencyManager::get(); diff --git a/interface/src/ui/DialogsManager.cpp b/interface/src/ui/DialogsManager.cpp index e2721f4ebb..517785a168 100644 --- a/interface/src/ui/DialogsManager.cpp +++ b/interface/src/ui/DialogsManager.cpp @@ -119,7 +119,6 @@ void DialogsManager::showLoginDialog() { } void DialogsManager::showLoginScreenDialog() { - LoginScreenDialog::showWithSelection(); } void DialogsManager::showUpdateDialog() { diff --git a/interface/src/ui/LoginScreenDialog.cpp b/interface/src/ui/LoginScreenDialog.cpp index 8ffc332ef1..7c1a1d7ea1 100644 --- a/interface/src/ui/LoginScreenDialog.cpp +++ b/interface/src/ui/LoginScreenDialog.cpp @@ -9,3 +9,21 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // #include "LoginScreenDialog.h" + +#include + +#include "ui/overlays/Overlays.h" +#include "ui/overlays/Overlay.h" +#include "avatar/AvatarManager.h" +#include "avatar/MyAvatar.h" +#include "DependencyManager.h" + +#include "Application.h" + +void LoginScreenDialog::createLoginScreen() { + Overlays& overlays = qApp->getOverlays(); + + //QVariantMap loginScreenOverlayProperties { + // { "url", } + //} +} \ No newline at end of file diff --git a/interface/src/ui/LoginScreenDialog.h b/interface/src/ui/LoginScreenDialog.h index ee82c81c44..ce94b4247b 100644 --- a/interface/src/ui/LoginScreenDialog.h +++ b/interface/src/ui/LoginScreenDialog.h @@ -8,6 +8,18 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -class HMDLoginScreenDialog : public QmlWindowClass { - virtual QString qmlSource() const override { return "hifi/dialogs/.qml"; } +#ifndef hifi_LoginScreenDialog_h +#define hifi_LoginScreenDialog_h + +#include + +class PointerEvent; + +class LoginScreenDialog : public Dependency, QObject { + LoginScreenDialog(); + void createLoginScreen(); + +public slots: }; + +#endif // hifi_LoginScreenDialog_h \ No newline at end of file diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index bcaa55d2f3..a2293c485d 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -65,6 +65,13 @@ DomainHandler::DomainHandler(QObject* parent) : // stop the refresh timer if redirected to the login screen domain connect(this, &DomainHandler::redirectToLoginScreenDomainURL, &_apiRefreshTimer, &QTimer::stop); + + + // stop the refresh timer if redirected to the login screen domain + connect(this, &DomainHandler::redirectToLoginScreenDomainURL, [this]() { + _isInLoginScreenState = true; + qCDebug(networking) << "Redirecting user to " << _loginScreenDomainURL; + }); } void DomainHandler::disconnect() { @@ -169,6 +176,11 @@ void DomainHandler::setErrorDomainURL(const QUrl& url) { return; } +void DomainHandler::setLoginScreenDomainURL(const QUrl& url) { + _loginScreenDomainURL = url; + return; +} + void DomainHandler::setSockAddr(const HifiSockAddr& sockAddr, const QString& hostname) { if (_sockAddr != sockAddr) { // we should reset on a sockAddr change @@ -395,12 +407,6 @@ void DomainHandler::setRedirectErrorState(QUrl errorUrl, QString reasonMessage, } } -void DomainHandler::redirectToLoginScreenDomainURL() { - _isInLoginScreenState = true; - qCDebug(networking) << "Redirecting user to " << _loginScreenDomainURL; - -} - void DomainHandler::requestDomainSettings() { qCDebug(networking) << "Requesting settings from domain server"; From b33267a63ae57edea80c380d024c1bdcd07a3e09 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Oct 2018 17:38:12 -0700 Subject: [PATCH 010/307] more wip --- interface/resources/qml/LoginDialog.qml | 5 +- .../qml/LoginDialog/LinkAccountBody.qml | 419 ++++++++++++++---- 2 files changed, 336 insertions(+), 88 deletions(-) diff --git a/interface/resources/qml/LoginDialog.qml b/interface/resources/qml/LoginDialog.qml index ebf4e9e3a6..d97b0d876d 100644 --- a/interface/resources/qml/LoginDialog.qml +++ b/interface/resources/qml/LoginDialog.qml @@ -47,7 +47,6 @@ Windows.ModalWindow { Loader { id: bodyLoader - source: loginDialog.isSteamRunning() ? "LoginDialog/SignInBody.qml" : "LoginDialog/LinkAccountBody.qml" } } @@ -86,4 +85,8 @@ Windows.ModalWindow { } } + Component.onCompleted: { + bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root }); + } + } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 758e8555cd..2df0bef665 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -27,14 +27,17 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false + property bool withSteam: false onKeyboardRaisedChanged: d.resize(); QtObject { id: d readonly property int minWidth: 480 + readonly property int minWidthButton: !root.isTablet ? 256 : 174 property int maxWidth: root.isTablet ? 1280 : Window.innerWidth readonly property int minHeight: 120 + readonly property int minHeightButton: !root.isTablet ? 56 : 42 property int maxHeight: root.isTablet ? 720 : Window.innerHeight function resize() { @@ -54,6 +57,42 @@ Item { } } + function toggleLoggingIn(loggingIn) { + // For the process of logging in. + if (withSteam) { + + } + else { + + } + } + + function toggleSignIn(signIn, isLogIn) { + // going to/from sign in/up dialog. + if (signIn) { + usernameField.visible = !isLogIn; + cantAccessContainer.visible = isLogIn; + if (isLogIn) { + emailField.placeholderText = "Username or Email"; + emailField.anchors.top = loginContainer.top; + emailField.anchors.topMargin = !root.isTablet ? 0.2 * root.pane.height : 0.24 * root.pane.height; + cantAccessContainer.anchors.topMargin = !root.isTablet ? 3.5 * hifi.dimensions.contentSpacing.y : hifi.dimensions.contentSpacing.y; + } else { + emailField.placeholderText = "Email"; + emailField.anchors.top = usernameField.bottom; + emailField.anchors.topMargin = 1.5 * hifi.dimensions.contentSpacing.y; + } + } + + splashContainer.visible = !signIn; + topContainer.height = signIn ? root.pane.height : 0.6 * topContainer.height; + bottomContainer.visible = !signIn; + dismissTextContainer.visible = !signIn; + topOpaqueRect.visible = signIn; + loginContainer.visible = signIn; + + } + function toggleLoading(isLoading) { linkAccountSpinner.visible = isLoading form.visible = !isLoading @@ -69,13 +108,22 @@ Item { height: 0.6 * root.pane.height onHeightChanged: d.resize(); onWidthChanged: d.resize(); + Rectangle { + id: topOpaqueRect + height: parent.height + width: parent.width + opacity: 0.7 + color: "black" + visible: false + } + Item { id: bannerContainer width: parent.width height: banner.height anchors { top: parent.top - topMargin: 0.17 * parent.height + topMargin: 85 } Image { id: banner @@ -84,44 +132,251 @@ Item { horizontalAlignment: Image.AlignHCenter } } - Text { - id: flavorText - text: qsTr("BE ANYWHERE, WITH ANYONE RIGHT NOW") - width: 0.48 * parent.width - anchors.centerIn: parent + Item { + id: loginContainer + width: parent.width + height: parent.height - (bannerContainer.height + 1.5 * hifi.dimensions.contentSpacing.y) anchors { top: bannerContainer.bottom - topMargin: 0.1 * parent.height + topMargin: 1.5 * hifi.dimensions.contentSpacing.y } - wrapMode: Text.WordWrap - lineHeight: 1 - color: "white" - font.family: "Raleway" - font.pixelSize: 55 - font.bold: true - lineHeightMode: Text.ProportionalHeight - horizontalAlignment: Text.AlignHCenter + visible: false + + HifiControlsUit.TextField { + id: usernameField + width: 0.254 * parent.width + placeholderText: "Username" + anchors { + top: parent.top + topMargin: 0.2 * parent.height + left: parent.left + leftMargin: (parent.width - usernameField.width) / 2 + } + visible: false + } + + HifiControlsUit.TextField { + id: emailField + width: 0.254 * parent.width + text: Settings.getValue("wallet/savedUsername", ""); + anchors { + top: parent.top + left: parent.left + leftMargin: (parent.width - emailField.width) / 2 + } + focus: true + placeholderText: "Username or Email" + activeFocusOnPress: true + onHeightChanged: d.resize(); onWidthChanged: d.resize(); + + Component.onCompleted: { + var savedUsername = Settings.getValue("wallet/savedUsername", ""); + emailField.text = savedUsername === "Unknown user" ? "" : savedUsername; + } + } + HifiControlsUit.TextField { + id: passwordField + width: 0.254 * parent.width + placeholderText: "Password" + activeFocusOnPress: true + echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password + anchors { + top: emailField.bottom + topMargin: 1.5 * hifi.dimensions.contentSpacing.y + left: parent.left + leftMargin: (parent.width - emailField.width) / 2 + } + + onFocusChanged: { + // root.text = ""; + // root.isPassword = true; + } + + Rectangle { + id: showPasswordHitbox + z: 10 + x: passwordField.width - ((passwordField.height) * 31 / 23) + width: parent.width - (parent.width - (parent.height * 31/16)) + height: parent.height + anchors { + right: parent.right + } + color: "transparent" + + Image { + id: showPasswordImage + width: passwordField.height * 16 / 23 + height: passwordField.height * 16 / 23 + anchors { + right: parent.right + rightMargin: 8 + top: parent.top + topMargin: passwordFieldMouseArea.showPassword ? 6 : 8 + bottom: parent.bottom + bottomMargin: passwordFieldMouseArea.showPassword ? 5 : 8 + } + source: passwordFieldMouseArea.showPassword ? "../../images/eyeClosed.svg" : "../../images/eyeOpen.svg" + MouseArea { + id: passwordFieldMouseArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + property bool showPassword: false + onClicked: { + showPassword = !showPassword; + } + } + } + + + + } + + Keys.onReturnPressed: { + signInBody.login() + } + + } + HifiControlsUit.CheckBox { + id: autoLogoutCheckbox + checked: !Settings.getValue("wallet/autoLogout", true) + text: "Keep Me Logged In" + boxSize: 18; + labelFontSize: 18; + color: hifi.colors.white; + anchors { + top: passwordField.bottom + topMargin: hifi.dimensions.contentSpacing.y + right: passwordField.right + } + onCheckedChanged: { + Settings.setValue("wallet/autoLogout", !checked); + if (checked) { + Settings.setValue("wallet/savedUsername", Account.username); + } else { + Settings.setValue("wallet/savedUsername", ""); + } + } + Component.onDestruction: { + Settings.setValue("wallet/autoLogout", !checked); + } + } + Item { + id: cancelContainer + width: cancelText.width + height: d.minHeightButton + anchors { + top: autoLogoutCheckbox.bottom + topMargin: hifi.dimensions.contentSpacing.y + left: parent.left + leftMargin: (parent.width - passwordField.width) / 2 + } + Text { + id: cancelText + anchors.centerIn: parent + text: qsTr("Cancel"); + + lineHeight: 1 + color: "white" + font.family: "Raleway" + font.pixelSize: 24 + font.bold: true + lineHeightMode: Text.ProportionalHeight + // horizontalAlignment: Text.AlignHCenter + } + MouseArea { + id: cancelArea + anchors.fill: parent + acceptedButtons: Qt.LeftButton + onClicked: { + toggleSignIn(false, true); + } + } + } + HifiControlsUit.Button { + id: loginButtonAtSignIn + width: d.minWidthButton + height: d.minHeightButton + text: qsTr("Log In") + fontSize: signUpButton.fontSize + color: hifi.buttons.none + // background: Rectangle { + // radius: hifi.buttons.radius + // + // } + anchors { + top: cancelContainer.top + right: passwordField.right + } + + onClicked: { + linkAccountBody.login() + } + } + Item { + id: cantAccessContainer + width: parent.width + height: cantAccessText.height + anchors { + top: cancelContainer.bottom + topMargin: 3.5 * hifi.dimensions.contentSpacing.y + } + visible: false + HifiStylesUit.ShortcutText { + id: cantAccessText + z: 10 + + text: " Can't access your account?" + + verticalAlignment: Text.AlignVCenter + horizontalAlignment: Text.AlignHCenter + linkColor: hifi.colors.blueAccent + onLinkActivated: loginDialog.openUrl(link) + } + } + } + Item { + id: splashContainer + width: parent.width + anchors.fill: parent - HifiControlsUit.Button { - id: signUpButton - text: qsTr("Sign Up") - width: 0.17 * parent.width - height: 0.068 * parent.height - color: hifi.buttons.blue - fontSize: 30 - anchors { - top: flavorText.bottom - topMargin: 0.1 * parent.height - left: parent.left - leftMargin: (parent.width - signUpButton.width) / 2 + visible: true + + Text { + id: flavorText + text: qsTr("BE ANYWHERE, WITH ANYONE RIGHT NOW") + width: 0.48 * parent.width + anchors.centerIn: parent + anchors { + top: bannerContainer.bottom + topMargin: 0.1 * parent.height + } + wrapMode: Text.WordWrap + lineHeight: 1 + color: "white" + font.family: "Raleway" + font.pixelSize: 48 + font.bold: true + lineHeightMode: Text.ProportionalHeight + horizontalAlignment: Text.AlignHCenter } - onClicked: { - bodyLoader.setSource("SignUpBody.qml") - if (!linkAccountBody.isTablet) { - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + HifiControlsUit.Button { + id: signUpButton + text: qsTr("Sign Up") + width: d.minWidthButton + height: d.minHeightButton + color: hifi.buttons.blue + fontSize: 30 + anchors { + bottom: parent.bottom + bottomMargin: 0.1 * parent.height + left: parent.left + leftMargin: (parent.width - signUpButton.width) / 2 + } + + onClicked: { + toggleSignIn(true, false); } } } @@ -134,58 +389,57 @@ Item { anchors.top: topContainer.bottom Rectangle { - height: root.pane.height - width: root.pane.width + id: bottomOpaqueRect + height: parent.height + width: parent.width opacity: 0.7 color: "black" } + Item { + id: bottomButtonsContainer - HifiControlsUit.Button { - id: loginButton - width: signUpButton.width - height: signUpButton.height - text: qsTr("Log In") - fontSize: signUpButton.fontSize - // background: Rectangle { - // radius: hifi.buttons.radius - // - // } - anchors { - top: parent.top - topMargin: 0.245 * parent.height - left: parent.left - leftMargin: (parent.width - loginButton.width) / 2 - } + width: parent.width + height: parent.height - onClicked: { - bodyLoader.setSource("SignInBody.qml") - if (!linkAccountBody.isTablet) { - loginDialog.bodyLoader.item.width = root.pane.width - loginDialog.bodyLoader.item.height = root.pane.height + HifiControlsUit.Button { + id: loginButton + width: signUpButton.width + height: signUpButton.height + text: qsTr("Log In") + fontSize: signUpButton.fontSize + // background: Rectangle { + // radius: hifi.buttons.radius + // + // } + anchors { + top: parent.top + topMargin: 0.245 * parent.height + left: parent.left + leftMargin: (parent.width - loginButton.width) / 2 + } + + onClicked: { + toggleSignIn(true, true); } } - } - HifiControlsUit.Button { - id: steamLoginButton - width: signUpButton.width - height: signUpButton.height - text: qsTr("Link Account") - fontSize: signUpButton.fontSize - color: hifi.buttons.black - anchors { - top: loginButton.bottom - topMargin: 0.04 * parent.height - left: parent.left - leftMargin: (parent.width - steamLoginButton.width) / 2 - } - - onClicked: { - if (loginDialog.isSteamRunning()) { - loginDialog.linkSteam(); + HifiControlsUit.Button { + id: steamLoginButton + width: signUpButton.width + height: signUpButton.height + text: qsTr("Steam Log In") + fontSize: signUpButton.fontSize + color: hifi.buttons.black + anchors { + top: loginButton.bottom + topMargin: 0.04 * parent.height + left: parent.left + leftMargin: (parent.width - steamLoginButton.width) / 2 } - if (!linkAccountBody.isTablet) { - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + + onClicked: { + if (loginDialog.isSteamRunning()) { + loginDialog.linkSteam(); + } } } } @@ -205,7 +459,7 @@ Item { lineHeight: 1 color: "white" font.family: "Raleway" - font.pixelSize: 20 + font.pixelSize: 24 font.bold: true lineHeightMode: Text.ProportionalHeight // horizontalAlignment: Text.AlignHCenter @@ -222,9 +476,6 @@ Item { } Component.onCompleted: { - root.title = qsTr("Sign Into High Fidelity") - root.iconText = "<" - //dont rise local keyboard keyboardEnabled = !root.isTablet && HMD.active; //but rise Tablet's one instead for Tablet interface @@ -234,12 +485,6 @@ Item { } d.resize(); - // if (failAfterSignUp) { - // mainTextContainer.text = "Account created successfully." - // flavorText.visible = true - // mainTextContainer.visible = true - // } - } Connections { @@ -293,7 +538,7 @@ Item { Keys.onPressed: { if (!visible) { - return + return; } switch (event.key) { From 736a78061dbb02205204fbf4d5719c20220307d0 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Oct 2018 17:44:03 -0700 Subject: [PATCH 011/307] button style change/cant access text --- interface/resources/qml/LoginDialog/LinkAccountBody.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 2df0bef665..02dbaef55b 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -298,7 +298,6 @@ Item { height: d.minHeightButton text: qsTr("Log In") fontSize: signUpButton.fontSize - color: hifi.buttons.none // background: Rectangle { // radius: hifi.buttons.radius // @@ -315,7 +314,7 @@ Item { Item { id: cantAccessContainer width: parent.width - height: cantAccessText.height + y: usernameField.height anchors { top: cancelContainer.bottom topMargin: 3.5 * hifi.dimensions.contentSpacing.y @@ -324,6 +323,8 @@ Item { HifiStylesUit.ShortcutText { id: cantAccessText z: 10 + anchors.centerIn: parent + font.pixelSize: 14 text: " Can't access your account?" From 1e418e9976d3b63d0ec2b8b88f1b40fc909ed338 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Oct 2018 17:52:03 -0700 Subject: [PATCH 012/307] some cleanup in LinkAccountBody --- .../resources/qml/LoginDialog/LinkAccountBody.qml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 02dbaef55b..038cc1da43 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -188,8 +188,6 @@ Item { } onFocusChanged: { - // root.text = ""; - // root.isPassword = true; } Rectangle { @@ -226,15 +224,10 @@ Item { } } } - - - } - Keys.onReturnPressed: { signInBody.login() } - } HifiControlsUit.CheckBox { id: autoLogoutCheckbox @@ -281,7 +274,6 @@ Item { font.pixelSize: 24 font.bold: true lineHeightMode: Text.ProportionalHeight - // horizontalAlignment: Text.AlignHCenter } MouseArea { id: cancelArea @@ -491,7 +483,7 @@ Item { Connections { target: loginDialog onHandleLoginCompleted: { - console.log("Login Succeeded, linking steam account") + console.log("Login Succeeded") var poppedUp = Settings.getValue("loginDialogPoppedUp", false); if (poppedUp) { console.log("[ENCOURAGELOGINDIALOG]: logging in") @@ -504,9 +496,7 @@ Item { if (loginDialog.isSteamRunning()) { loginDialog.linkSteam() } else { - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height + // TODO you are now logged in } } onHandleLoginFailed: { From 137b240a474da07dec3b3a4925c8f6c98216fc17 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 22 Oct 2018 20:03:12 -0700 Subject: [PATCH 013/307] more changes to functions --- .../qml/LoginDialog/LinkAccountBody.qml | 28 +++++++++++++------ .../qml/dialogs/TabletLoginDialog.qml | 4 ++- 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 038cc1da43..89ebde36cc 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -27,6 +27,7 @@ Item { property bool keyboardRaised: false property bool punctuationMode: false + property bool isLogIn: false property bool withSteam: false onKeyboardRaisedChanged: d.resize(); @@ -57,9 +58,18 @@ Item { } } - function toggleLoggingIn(loggingIn) { + function login() { + if (linkAccountBody.isLogIn) { + loginDialog.login(emailField.text, passwordField.text); + } else { + loginDialog.signup(emailField.text, usernameField.text, passwordField.text); + } + linkAccountBody.toggleLoggingIn(); + } + + function toggleLoggingIn() { // For the process of logging in. - if (withSteam) { + if (linkAccountBody.withSteam) { } else { @@ -69,6 +79,7 @@ Item { function toggleSignIn(signIn, isLogIn) { // going to/from sign in/up dialog. + linkAccountBody.isLogIn = isLogIn; if (signIn) { usernameField.visible = !isLogIn; cantAccessContainer.visible = isLogIn; @@ -188,6 +199,7 @@ Item { } onFocusChanged: { + root.isPassword = true; } Rectangle { @@ -226,20 +238,20 @@ Item { } } Keys.onReturnPressed: { - signInBody.login() + linkAccountBody.login() } } HifiControlsUit.CheckBox { id: autoLogoutCheckbox - checked: !Settings.getValue("wallet/autoLogout", true) - text: "Keep Me Logged In" + checked: !Settings.getValue("wallet/autoLogout", false); + text: qsTr("Keep Me Logged In") boxSize: 18; labelFontSize: 18; color: hifi.colors.white; anchors { - top: passwordField.bottom - topMargin: hifi.dimensions.contentSpacing.y - right: passwordField.right + top: passwordField.bottom; + topMargin: hifi.dimensions.contentSpacing.y; + right: passwordField.right; } onCheckedChanged: { Settings.setValue("wallet/autoLogout", !checked); diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index c85b2b2ba0..fef86a7241 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -122,7 +122,6 @@ TabletModalWindow { id: bodyLoader anchors.fill: parent anchors.horizontalCenter: parent.horizontalCenter - source: loginDialog.isSteamRunning() ? "../LoginDialog/SignInBody.qml" : "../LoginDialog/LinkAccountBody.qml" } } } @@ -173,4 +172,7 @@ TabletModalWindow { break } } + Component.onCompleted: { + bodyLoader.setSource("../LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root }); + } } From 162502923ab624aabd2a92c7be84ba3f1774c9f7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Oct 2018 10:00:35 -0700 Subject: [PATCH 014/307] image for steam login button --- .../qml/LoginDialog/LinkAccountBody.qml | 104 ++++++++++++++++-- 1 file changed, 93 insertions(+), 11 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 74cf6164bb..323fa785f0 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -65,10 +65,10 @@ Item { } else { loginDialog.signup(emailField.text, usernameField.text, passwordField.text); } - linkAccountBody.toggleLoggingIn(); + linkAccountBody.toggleLoggingIn(false); } - function toggleLoggingIn() { + function toggleLoggingIn(loggedIn) { // For the process of logging in. if (linkAccountBody.withSteam) { @@ -373,7 +373,7 @@ Item { width: d.minWidthButton height: d.minHeightButton color: hifi.buttons.blue - fontSize: 30 + fontSize: 21 anchors { bottom: parent.bottom bottomMargin: 0.1 * parent.height @@ -428,13 +428,13 @@ Item { toggleSignIn(true, true); } } - HifiControlsUit.Button { - id: steamLoginButton + Button { + id: steamLoginButton; width: signUpButton.width height: signUpButton.height - text: qsTr("Steam Log In") - fontSize: signUpButton.fontSize - color: hifi.buttons.black + property int color: hifi.buttons.black; + property int colorScheme: hifi.colorSchemes.light; + anchors { top: loginButton.bottom topMargin: 0.04 * parent.height @@ -442,9 +442,91 @@ Item { leftMargin: (parent.width - steamLoginButton.width) / 2 } + enabled: root.hasPermissionToRezThis && + MyAvatar.skeletonModelURL !== root.itemHref && + !root.wornEntityID && + root.valid; + + onHoveredChanged: { + if (hovered) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + + onFocusChanged: { + if (focus) { + Tablet.playSound(TabletEnums.ButtonHover); + } + } + onClicked: { - if (loginDialog.isSteamRunning()) { - loginDialog.linkSteam(); + Tablet.playSound(TabletEnums.ButtonClick); + } + + style: OriginalStyles.ButtonStyle { + background: Rectangle { + radius: 4; + gradient: Gradient { + GradientStop { + position: 0.2 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorStart[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorStart[control.color] + } + } + } + GradientStop { + position: 1.0 + color: { + if (!control.enabled) { + hifi.buttons.disabledColorFinish[control.colorScheme] + } else if (control.pressed) { + hifi.buttons.pressedColor[control.color] + } else if (control.hovered) { + hifi.buttons.hoveredColor[control.color] + } else { + hifi.buttons.colorFinish[control.color] + } + } + } + } + } + + label: Item { + Image { + id: steamIcon; + anchors.right: steamIconLabel.left; + anchors.rightMargin: 5; + anchors.verticalCenter: parent.verticalCenter; + source: "images/steam.svg"; + horizontalAlignment: Image.AlignHCenter; + sourceSize.width: signUpButton.fontSize + 3 + sourceSize.height: signUpButton.fontSize + 3 + } + TextMetrics { + id: steamIconLabelTextMetrics; + font: steamIconLabel.font; + text: steamIconLabel.text; + } + HifiStylesUit.RalewayBold { + id: steamIconLabel; + text: "Steam Log In" + anchors.verticalCenter: parent.verticalCenter; + width: steamIconLabelTextMetrics.width; + x: parent.width/2 - steamIconLabelTextMetrics.width/2 + steamIcon.width/2; + size: signUpButton.fontSize; + font.capitalization: Font.AllUppercase; + verticalAlignment: Text.AlignVCenter; + horizontalAlignment: Text.AlignHCenter; + color: enabled ? hifi.buttons.textColor[control.color] + : hifi.buttons.disabledTextColor[control.colorScheme] + } } } } @@ -468,7 +550,7 @@ Item { font.pixelSize: 24 font.bold: true lineHeightMode: Text.ProportionalHeight - // horizontalAlignment: Text.AlignHCenter + horizontalAlignment: Text.AlignHCenter } MouseArea { id: dismissMouseArea From 2a0a099397e4ed8cf079c061a2d56de6843d11a1 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Oct 2018 10:04:21 -0700 Subject: [PATCH 015/307] adding steam svg --- interface/resources/images/steam.svg | 4 ++++ interface/resources/qml/LoginDialog/LinkAccountBody.qml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) create mode 100644 interface/resources/images/steam.svg diff --git a/interface/resources/images/steam.svg b/interface/resources/images/steam.svg new file mode 100644 index 0000000000..642941abf3 --- /dev/null +++ b/interface/resources/images/steam.svg @@ -0,0 +1,4 @@ + + + + diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 323fa785f0..570aea9635 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -504,7 +504,7 @@ Item { anchors.right: steamIconLabel.left; anchors.rightMargin: 5; anchors.verticalCenter: parent.verticalCenter; - source: "images/steam.svg"; + source: "../../images/steam.svg"; horizontalAlignment: Image.AlignHCenter; sourceSize.width: signUpButton.fontSize + 3 sourceSize.height: signUpButton.fontSize + 3 From 56656a9079594799fb7bcbf16e80ba5f0e541bc8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Oct 2018 10:46:19 -0700 Subject: [PATCH 016/307] adding button font modularity/using cairo --- .../qml/LoginDialog/LinkAccountBody.qml | 60 ++-- .../qml/LoginDialog/LoggingInDialog.qml | 9 - .../resources/qml/LoginDialog/SignUpBody.qml | 280 ------------------ .../resources/qml/controlsUit/Button.qml | 13 +- 4 files changed, 40 insertions(+), 322 deletions(-) delete mode 100644 interface/resources/qml/LoginDialog/LoggingInDialog.qml delete mode 100644 interface/resources/qml/LoginDialog/SignUpBody.qml diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 570aea9635..9ee81adadf 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -16,6 +16,8 @@ import QtQuick.Controls.Styles 1.4 as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 + Item { id: linkAccountBody clip: true @@ -71,7 +73,7 @@ Item { function toggleLoggingIn(loggedIn) { // For the process of logging in. if (linkAccountBody.withSteam) { - + loginContainer.visible = false; } else { @@ -85,11 +87,15 @@ Item { usernameField.visible = !isLogIn; cantAccessContainer.visible = isLogIn; if (isLogIn) { + loginButtonAtSignIn.text = "Log In"; + loginButtonAtSignIn.color = hifi.buttons.black; emailField.placeholderText = "Username or Email"; emailField.anchors.top = loginContainer.top; emailField.anchors.topMargin = !root.isTablet ? 0.2 * root.pane.height : 0.24 * root.pane.height; cantAccessContainer.anchors.topMargin = !root.isTablet ? 3.5 * hifi.dimensions.contentSpacing.y : hifi.dimensions.contentSpacing.y; } else { + loginButtonAtSignIn.text = "Sign Up"; + loginButtonAtSignIn.color = hifi.buttons.blue; emailField.placeholderText = "Email"; emailField.anchors.top = usernameField.bottom; emailField.anchors.topMargin = 1.5 * hifi.dimensions.contentSpacing.y; @@ -124,7 +130,7 @@ Item { id: topOpaqueRect height: parent.height width: parent.width - opacity: 0.7 + opacity: 0.9 color: "black" visible: false } @@ -156,7 +162,8 @@ Item { HifiControlsUit.TextField { id: usernameField - width: 0.254 * parent.width + width: banner.width + font.family: "Cairo" placeholderText: "Username" anchors { top: parent.top @@ -169,7 +176,8 @@ Item { HifiControlsUit.TextField { id: emailField - width: 0.254 * parent.width + width: banner.width + font.family: "Cairo" text: Settings.getValue("wallet/savedUsername", ""); anchors { top: parent.top @@ -188,7 +196,8 @@ Item { } HifiControlsUit.TextField { id: passwordField - width: 0.254 * parent.width + width: banner.width + font.family: "Cairo" placeholderText: "Password" activeFocusOnPress: true echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password @@ -247,6 +256,7 @@ Item { checked: !Settings.getValue("wallet/autoLogout", false); text: qsTr("Keep Me Logged In") boxSize: 18; + labelFontFamily: "Cairo" labelFontSize: 18; color: hifi.colors.white; anchors { @@ -283,8 +293,9 @@ Item { lineHeight: 1 color: "white" - font.family: "Raleway" + font.family: "Cairo" font.pixelSize: 24 + font.capitalization: Font.AllUppercase; font.bold: true lineHeightMode: Text.ProportionalHeight } @@ -302,11 +313,9 @@ Item { width: d.minWidthButton height: d.minHeightButton text: qsTr("Log In") + fontFamily: "Cairo" fontSize: signUpButton.fontSize - // background: Rectangle { - // radius: hifi.buttons.radius - // - // } + fontBold: true anchors { top: cancelContainer.top right: passwordField.right @@ -329,6 +338,7 @@ Item { id: cantAccessText z: 10 anchors.centerIn: parent + font.family: "Cairo" font.pixelSize: 14 text: " Can't access your account?" @@ -354,13 +364,13 @@ Item { width: 0.48 * parent.width anchors.centerIn: parent anchors { - top: bannerContainer.bottom + top: banner.bottom topMargin: 0.1 * parent.height } wrapMode: Text.WordWrap lineHeight: 1 color: "white" - font.family: "Raleway" + font.family: "Cairo" font.pixelSize: 48 font.bold: true lineHeightMode: Text.ProportionalHeight @@ -373,7 +383,9 @@ Item { width: d.minWidthButton height: d.minHeightButton color: hifi.buttons.blue + fontFamily: "Cairo" fontSize: 21 + fontBold: true anchors { bottom: parent.bottom bottomMargin: 0.1 * parent.height @@ -398,7 +410,7 @@ Item { id: bottomOpaqueRect height: parent.height width: parent.width - opacity: 0.7 + opacity: 0.9 color: "black" } Item { @@ -412,11 +424,9 @@ Item { width: signUpButton.width height: signUpButton.height text: qsTr("Log In") + fontFamily: "Cairo" fontSize: signUpButton.fontSize - // background: Rectangle { - // radius: hifi.buttons.radius - // - // } + fontBold: true anchors { top: parent.top topMargin: 0.245 * parent.height @@ -442,11 +452,6 @@ Item { leftMargin: (parent.width - steamLoginButton.width) / 2 } - enabled: root.hasPermissionToRezThis && - MyAvatar.skeletonModelURL !== root.itemHref && - !root.wornEntityID && - root.valid; - onHoveredChanged: { if (hovered) { Tablet.playSound(TabletEnums.ButtonHover); @@ -514,16 +519,18 @@ Item { font: steamIconLabel.font; text: steamIconLabel.text; } - HifiStylesUit.RalewayBold { + Text { id: steamIconLabel; text: "Steam Log In" anchors.verticalCenter: parent.verticalCenter; width: steamIconLabelTextMetrics.width; x: parent.width/2 - steamIconLabelTextMetrics.width/2 + steamIcon.width/2; - size: signUpButton.fontSize; font.capitalization: Font.AllUppercase; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter; + font.family: "Cairo" + font.pixelSize: signUpButton.fontSize; + font.bold: true color: enabled ? hifi.buttons.textColor[control.color] : hifi.buttons.disabledTextColor[control.colorScheme] } @@ -546,7 +553,7 @@ Item { lineHeight: 1 color: "white" - font.family: "Raleway" + font.family: "Cairo" font.pixelSize: 24 font.bold: true lineHeightMode: Text.ProportionalHeight @@ -605,14 +612,11 @@ Item { UserActivityLogger.logAction("encourageLoginDialog", data); Settings.setValue("loginDialogPoppedUp", false); } - flavorText.visible = true - mainTextContainer.visible = true toggleLoading(false) } onHandleLinkCompleted: { console.log("Link Succeeded") - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack" : true }) bodyLoader.item.width = root.pane.width bodyLoader.item.height = root.pane.height } diff --git a/interface/resources/qml/LoginDialog/LoggingInDialog.qml b/interface/resources/qml/LoginDialog/LoggingInDialog.qml deleted file mode 100644 index e00f1178b1..0000000000 --- a/interface/resources/qml/LoginDialog/LoggingInDialog.qml +++ /dev/null @@ -1,9 +0,0 @@ -// -// LoggingInDialog.qml -// -// Created by Wayne Chen -// Copyright 2018 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 -// diff --git a/interface/resources/qml/LoginDialog/SignUpBody.qml b/interface/resources/qml/LoginDialog/SignUpBody.qml deleted file mode 100644 index 107fa8348f..0000000000 --- a/interface/resources/qml/LoginDialog/SignUpBody.qml +++ /dev/null @@ -1,280 +0,0 @@ -// -// SignUpBody.qml -// -// Created by Wayne Chen on Oct 18 2018 -// 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 Hifi 1.0 -import QtQuick 2.7 -import QtQuick.Controls 1.4 - -import controlsUit 1.0 -import stylesUit 1.0 - -Item { - id: signupBody - clip: true - height: root.pane.height - width: root.pane.width - - function signup() { - mainTextContainer.visible = false - toggleLoading(true) - loginDialog.signup(emailField.text, usernameField.text, passwordField.text) - } - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - - QtObject { - id: d - readonly property int minWidth: 480 - readonly property int maxWidth: 1280 - readonly property int minHeight: 120 - readonly property int maxHeight: 720 - - function resize() { - var targetWidth = Math.max(titleWidth, form.contentWidth); - var targetHeight = hifi.dimensions.contentSpacing.y + mainTextContainer.height + - 4 * hifi.dimensions.contentSpacing.y + form.height; - - parent.width = root.width = Math.max(d.minWidth, Math.min(d.maxWidth, targetWidth)); - parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) - + (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : 0); - } - } - - function toggleLoading(isLoading) { - linkAccountSpinner.visible = isLoading - form.visible = !isLoading - - leftButton.visible = !isLoading - buttons.visible = !isLoading - } - - BusyIndicator { - id: linkAccountSpinner - - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - running: true - - width: 48 - height: 48 - } - - ShortcutText { - id: mainTextContainer - anchors { - top: parent.top - left: parent.left - margins: 0 - topMargin: hifi.dimensions.contentSpacing.y - } - - visible: false - - text: qsTr("There was an unknown error while creating your account.") - wrapMode: Text.WordWrap - color: hifi.colors.redAccent - horizontalAlignment: Text.AlignLeft - } - - Column { - id: form - width: parent.width - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - anchors { - top: mainTextContainer.bottom - topMargin: 2 * hifi.dimensions.contentSpacing.y - } - spacing: 2 * hifi.dimensions.contentSpacing.y - - TextField { - id: emailField - width: parent.width - label: "Email" - activeFocusOnPress: true - onFocusChanged: { - root.text = ""; - } - } - - TextField { - id: usernameField - width: parent.width - label: "Username" - activeFocusOnPress: true - - ShortcutText { - anchors { - verticalCenter: parent.textFieldLabel.verticalCenter - left: parent.textFieldLabel.right - leftMargin: 10 - } - - text: qsTr("No spaces / special chars.") - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - color: hifi.colors.blueAccent - onFocusChanged: { - root.text = ""; - } - } - } - - TextField { - id: passwordField - width: parent.width - label: "Password" - echoMode: TextInput.Password - activeFocusOnPress: true - - ShortcutText { - anchors { - verticalCenter: parent.textFieldLabel.verticalCenter - left: parent.textFieldLabel.right - leftMargin: 10 - } - - text: qsTr("At least 6 characters") - - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - - color: hifi.colors.blueAccent - } - - onFocusChanged: { - root.text = ""; - root.isPassword = focus - } - - Keys.onReturnPressed: signupBody.signup() - } - - Row { - id: leftButton - anchors.horizontalCenter: parent.horizontalCenter - - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Existing User") - - onClicked: { - bodyLoader.setSource("LinkAccountBody.qml") - if (!root.isTablet) { - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } - } - } - - Row { - id: buttons - anchors.horizontalCenter: parent.horizontalCenter - spacing: hifi.dimensions.contentSpacing.x - onHeightChanged: d.resize(); onWidthChanged: d.resize(); - - Button { - id: linkAccountButton - anchors.verticalCenter: parent.verticalCenter - width: 200 - - text: qsTr("Sign Up") - color: hifi.buttons.blue - - onClicked: signupBody.signup() - } - - Button { - anchors.verticalCenter: parent.verticalCenter - - text: qsTr("Cancel") - - onClicked: root.tryDestroy() - } - } - } - - Component.onCompleted: { - root.title = qsTr("Create an Account") - root.iconText = "<" - //dont rise local keyboard - keyboardEnabled = !root.isTablet && HMD.active; - //but rise Tablet's one instead for Tablet interface - if (root.isTablet) { - root.keyboardEnabled = HMD.active; - root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) - } - d.resize(); - - emailField.forceActiveFocus(); - } - - Connections { - target: loginDialog - onHandleSignupCompleted: { - console.log("Sign Up Succeeded"); - - // now that we have an account, login with that username and password - loginDialog.login(usernameField.text, passwordField.text) - } - onHandleSignupFailed: { - console.log("Sign Up Failed") - toggleLoading(false) - - mainTextContainer.text = errorString - mainTextContainer.visible = true - - d.resize(); - } - onHandleLoginCompleted: { - bodyLoader.setSource("WelcomeBody.qml", { "welcomeBack": false }) - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - onHandleLoginFailed: { - // we failed to login, show the LoginDialog so the user will try again - bodyLoader.setSource("LinkAccountBody.qml", { "failAfterSignUp": true }) - if (!root.isTablet) { - bodyLoader.item.width = root.pane.width - bodyLoader.item.height = root.pane.height - } - } - } - - Keys.onPressed: { - if (!visible) { - return - } - - switch (event.key) { - case Qt.Key_Enter: - case Qt.Key_Return: - event.accepted = true - signupBody.signup() - break - } - } -} diff --git a/interface/resources/qml/controlsUit/Button.qml b/interface/resources/qml/controlsUit/Button.qml index 6ea7ce4b4c..2fd98e04a6 100644 --- a/interface/resources/qml/controlsUit/Button.qml +++ b/interface/resources/qml/controlsUit/Button.qml @@ -19,7 +19,9 @@ Original.Button { property int color: 0 property int colorScheme: hifi.colorSchemes.light + property string fontFamily: "Raleway" property int fontSize: hifi.fontSizes.buttonLabel + property bool fontBold: true property int radius: hifi.buttons.radius property alias implicitTextWidth: buttonText.implicitWidth property string buttonGlyph: ""; @@ -35,13 +37,13 @@ Original.Button { Tablet.playSound(TabletEnums.ButtonHover); } } - + onFocusChanged: { if (focus) { Tablet.playSound(TabletEnums.ButtonHover); } } - + onClicked: { Tablet.playSound(TabletEnums.ButtonClick); } @@ -106,17 +108,18 @@ Original.Button { horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; } - RalewayBold { + Text { id: buttonText; anchors.centerIn: parent; font.capitalization: control.fontCapitalization color: enabled ? hifi.buttons.textColor[control.color] : hifi.buttons.disabledTextColor[control.colorScheme] - size: control.fontSize + font.family: control.fontFamily + font.pixelSize: control.fontSize + font.bold: control.fontBold verticalAlignment: Text.AlignVCenter horizontalAlignment: Text.AlignHCenter text: control.text } } } - From d5b7fe62dbde7be2b6a3e19b17953874ea0d0d34 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Oct 2018 11:00:42 -0700 Subject: [PATCH 017/307] modularizing qml font in login --- .../qml/LoginDialog/LinkAccountBody.qml | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 9ee81adadf..71ae881313 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -25,6 +25,8 @@ Item { width: root.pane.width property bool isTablet: root.isTablet property bool failAfterSignUp: false + property string fontFamily: "Cairo" + property bool fontBold: true property bool keyboardEnabled: false property bool keyboardRaised: false @@ -163,7 +165,7 @@ Item { HifiControlsUit.TextField { id: usernameField width: banner.width - font.family: "Cairo" + font.family: linkAccountBody.fontFamily placeholderText: "Username" anchors { top: parent.top @@ -177,7 +179,7 @@ Item { HifiControlsUit.TextField { id: emailField width: banner.width - font.family: "Cairo" + font.family: linkAccountBody.fontFamily text: Settings.getValue("wallet/savedUsername", ""); anchors { top: parent.top @@ -197,7 +199,7 @@ Item { HifiControlsUit.TextField { id: passwordField width: banner.width - font.family: "Cairo" + font.family: linkAccountBody.fontFamily placeholderText: "Password" activeFocusOnPress: true echoMode: passwordFieldMouseArea.showPassword ? TextInput.Normal : TextInput.Password @@ -256,7 +258,7 @@ Item { checked: !Settings.getValue("wallet/autoLogout", false); text: qsTr("Keep Me Logged In") boxSize: 18; - labelFontFamily: "Cairo" + labelFontFamily: linkAccountBody.fontFamily labelFontSize: 18; color: hifi.colors.white; anchors { @@ -293,10 +295,10 @@ Item { lineHeight: 1 color: "white" - font.family: "Cairo" + font.family: linkAccountBody.fontFamily font.pixelSize: 24 font.capitalization: Font.AllUppercase; - font.bold: true + font.bold: linkAccountBody.fontBold lineHeightMode: Text.ProportionalHeight } MouseArea { @@ -313,9 +315,9 @@ Item { width: d.minWidthButton height: d.minHeightButton text: qsTr("Log In") - fontFamily: "Cairo" + fontFamily: linkAccountBody.fontFamily fontSize: signUpButton.fontSize - fontBold: true + fontBold: linkAccountBody.fontBold anchors { top: cancelContainer.top right: passwordField.right @@ -338,7 +340,7 @@ Item { id: cantAccessText z: 10 anchors.centerIn: parent - font.family: "Cairo" + font.family: linkAccountBody.fontFamily font.pixelSize: 14 text: " Can't access your account?" @@ -370,9 +372,9 @@ Item { wrapMode: Text.WordWrap lineHeight: 1 color: "white" - font.family: "Cairo" + font.family: linkAccountBody.fontFamily font.pixelSize: 48 - font.bold: true + font.bold: linkAccountBody.fontBold lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter } @@ -383,9 +385,9 @@ Item { width: d.minWidthButton height: d.minHeightButton color: hifi.buttons.blue - fontFamily: "Cairo" - fontSize: 21 - fontBold: true + fontFamily: linkAccountBody.fontFamily + fontSize: 24 + fontBold: linkAccountBody.fontBold anchors { bottom: parent.bottom bottomMargin: 0.1 * parent.height @@ -424,12 +426,12 @@ Item { width: signUpButton.width height: signUpButton.height text: qsTr("Log In") - fontFamily: "Cairo" + fontFamily: linkAccountBody.fontFamily fontSize: signUpButton.fontSize - fontBold: true + fontBold: linkAccountBody.fontBold anchors { top: parent.top - topMargin: 0.245 * parent.height + topMargin: (parent.height / 2) - loginButton.height left: parent.left leftMargin: (parent.width - loginButton.width) / 2 } @@ -447,7 +449,7 @@ Item { anchors { top: loginButton.bottom - topMargin: 0.04 * parent.height + topMargin: hifi.dimensions.contentSpacing.y left: parent.left leftMargin: (parent.width - steamLoginButton.width) / 2 } @@ -528,9 +530,9 @@ Item { font.capitalization: Font.AllUppercase; verticalAlignment: Text.AlignVCenter; horizontalAlignment: Text.AlignHCenter; - font.family: "Cairo" + font.family: linkAccountBody.fontFamily font.pixelSize: signUpButton.fontSize; - font.bold: true + font.bold: linkAccountBody.fontBold color: enabled ? hifi.buttons.textColor[control.color] : hifi.buttons.disabledTextColor[control.colorScheme] } @@ -553,9 +555,9 @@ Item { lineHeight: 1 color: "white" - font.family: "Cairo" + font.family: linkAccountBody.fontFamily font.pixelSize: 24 - font.bold: true + font.bold: linkAccountBody.fontBold lineHeightMode: Text.ProportionalHeight horizontalAlignment: Text.AlignHCenter } From c28f0fd23298300e71209d7f67e74c5c31323e52 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 23 Oct 2018 16:22:42 -0700 Subject: [PATCH 018/307] updating qml, adding new glyphs font --- .../hifi-glyphs-1.32/fonts/hifi-glyphs.woff | Bin 21548 -> 0 bytes .../fonts/hifi-glyphs.eot | Bin 33678 -> 33958 bytes .../fonts/hifi-glyphs.svg | 1 + .../fonts/hifi-glyphs.ttf | Bin 33500 -> 33780 bytes .../hifi-glyphs-1.33/fonts/hifi-glyphs.woff | Bin 0 -> 21816 bytes .../icons-reference.html | 8 + .../styles.css | 3 + interface/resources/fonts/hifi-glyphs.ttf | Bin 33500 -> 33780 bytes .../resources/{images => icons}/steam.svg | 0 interface/resources/qml/LoginDialog.qml | 2 +- .../qml/LoginDialog/LinkAccountBody.qml | 366 +++++++----- .../resources/qml/LoginDialog/SignInBody.qml | 559 +++++++++++++++--- .../resources/qml/controlsUit/Button.qml | 24 +- .../resources/qml/stylesUit/HifiConstants.qml | 1 + 14 files changed, 733 insertions(+), 231 deletions(-) delete mode 100644 interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff rename interface/resources/fonts/{hifi-glyphs-1.32 => hifi-glyphs-1.33}/fonts/hifi-glyphs.eot (94%) rename interface/resources/fonts/{hifi-glyphs-1.32 => hifi-glyphs-1.33}/fonts/hifi-glyphs.svg (98%) rename interface/resources/fonts/{hifi-glyphs-1.32 => hifi-glyphs-1.33}/fonts/hifi-glyphs.ttf (94%) create mode 100644 interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff rename interface/resources/fonts/{hifi-glyphs-1.32 => hifi-glyphs-1.33}/icons-reference.html (99%) rename interface/resources/fonts/{hifi-glyphs-1.32 => hifi-glyphs-1.33}/styles.css (99%) rename interface/resources/{images => icons}/steam.svg (100%) diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.woff deleted file mode 100644 index 534f8e5623b776bc9e4ffe0caef6bf895ff3c105..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21548 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 N6x$a+Y_7>>{|}strOW^T delta 562 zcmYk2O=uHg5Qd-G?5+)wL{XyHfDKr4uraY+(jPns)s}*^f)IMBm8Fe|(AZ#dNK4X` zC`cg`%3=|$pqCztvN!QiDjxJM3Kr}^deMLfJqU#@yRP5|!}rV#^Ulxain97h(N`b9 zZgWk~s2e-2{k1F2#^Q}*IyAR|*yz=3BfQ1+p95VJH&eY6Z>}$ZOaj@NqFLH(Y@Gn* z8z|MhSuQya0cbt}I`VT1ck<6ZzXrVpGt9sDAu;5WZn8Hx5Ti=l`%=-?&Dd^lSA$k z@F<2VS$9vj(9>PoGwct6VX-4xSnRWUwR?1wPtGlBr&Yf`h{SB(n$8EFAd=5!q$uW rnDxEhCF}Olr5>?|8l76)n-_5$6t8y}IPC>pKfq&a`C(`1zdIcQlOTHb diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg b/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg similarity index 98% rename from interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg rename to interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg index 4f1fb2e690..554a3999be 100644 --- a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.svg +++ b/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg @@ -154,4 +154,5 @@ + diff --git a/interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.ttf similarity index 94% rename from interface/resources/fonts/hifi-glyphs-1.32/fonts/hifi-glyphs.ttf rename to interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.ttf index e85c193fd02bab6f2eab51e12080a5e960a66e4b..b61455a9f11a4703fbd55e8a319b2b2b929ab632 100644 GIT binary patch delta 810 zcmY+DOH30%7{~wLcDuWzJlryDjnLXIwu|yw`T|YC2a1@ekU&rqm266(Q3^<_!4`x_ zgagKdYrumiJQ**98l#B`i7}o?JW2uwjvmx_kds7AOD|3`ncrdNm+v)OkGS9S+%yP4 z31(nHcSlF>rAci)2+VE(s5*c4Tn9cu0A}9-aCh}I`nx_{OapWkKwmUrjJ*!L`U7y^ z04_abB**slU;$=V07yf__Xkg(X|n^uGa&Un9y11H>6QUt3qUv?k4+oAPTK%607v49 z)P$%1P&+^?0ELG~qeg1i90#le0O<*1VhlTU6QGt{KqE#Xc6*Sm1DP3s-j0nXQzLV0 z5n!qX5GL};5zsXAa=E`OvI853-1@r1R`*Yitv;nzS~A<{28FUO=mG`2`8yUMzpL^R zPzshjRe{fEFG0e-?ZI{^%Pj3gi`hQDjIvRyM%;;e*~h|<-KNf|x<25nVa!>@DwqNg%!#z7)nH-2jNoCzdV#I5;a7nL z*oc{*(;lrG$8)9Ti@-y{n*v**V57hS6zrO78Lt`0^oRk}qY1jXkg4SZ^-a3@K9e%* zCOx#2y*yb*9GThlRHe0~IGKtWiTdQ7yT*9TeE!g>ErP%Ug*N6s0@hoXzx+QTZ1o?3 CE1$>! delta 517 zcmX|+F=!KU6o4ULBebB+U}IxF(ni#!8Y!qPD4|GeIciLVo(U#H+FlxM z5fw7|w?#+=C&yB`O&mmVaOoyGbm$;X2A4Vrg~}zt2aor?<@esV`$XG*tW^=<01pW; zkxJdXwlHzXgyj!F^q(DjkCZukjUjSRa=kC4#;8PM9wLd`_3I31=$3# z=JK<4X>W25j46=Qj$JOWE4M*L{DY_}G<*mz@4B{I!JJJk59uR0!1YOd;89`b}OFNc# zG%zaOw0=-L$2ch(crVOGQ9WAhk4Aacia;mCmo^)OXtdcRBwcE2u`ha^q<=>5Kqt)G zHXDTb+Gdk5e^uT+qGGOX3=_jrtL~}baLkGg#m|hWu1df9?T)ErrB~LxTa^Lvo-Xwm UzK+^lIWDVeYo#ajUp`U)2MTX-;{X5v diff --git a/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff new file mode 100644 index 0000000000000000000000000000000000000000..842065af361c0063b156f7577f012e881e897c8d GIT binary patch literal 21816 zcmV(;K-<4}Pew*hR8&s@097~u3jhEB0C?;G0RR91000000000000000000000000( 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

<4 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 ru8XPoL>mp8tUkD^RB>efn)3v5!9{!kc${NkW +

  • +
    + +
  • Character mapping

      @@ -1194,6 +1198,10 @@
      +
    • +
      + +
    + diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 01395368b0..a8bcabbfea 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -23,6 +23,7 @@ + diff --git a/scripts/system/html/gridControls.html b/scripts/system/html/gridControls.html index 4be002619a..8d6ee34bc0 100644 --- a/scripts/system/html/gridControls.html +++ b/scripts/system/html/gridControls.html @@ -16,6 +16,7 @@ + diff --git a/scripts/system/html/js/entityList.js b/scripts/system/html/js/entityList.js index 84ad59df36..eb3e9ed882 100644 --- a/scripts/system/html/js/entityList.js +++ b/scripts/system/html/js/entityList.js @@ -1286,8 +1286,9 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); - document.addEventListener("contextmenu", function (event) { + document.addEventListener("contextmenu", function(event) { entityListContextMenu.close(); // Disable default right-click context menu which is not visible in the HMD and makes it seem like the app has locked diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d3e0751732..6dc563b102 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -3479,6 +3479,7 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); // Disable right-click context menu which is not visible in the HMD and makes it seem like the app has locked document.addEventListener("contextmenu", function(event) { diff --git a/scripts/system/html/js/gridControls.js b/scripts/system/html/js/gridControls.js index b2d5988938..c2183130e9 100644 --- a/scripts/system/html/js/gridControls.js +++ b/scripts/system/html/js/gridControls.js @@ -108,6 +108,7 @@ function loaded() { }); augmentSpinButtons(); + disableDragDrop(); EventBridge.emitWebEvent(JSON.stringify({ type: 'init' })); }); diff --git a/scripts/system/html/js/utils.js b/scripts/system/html/js/utils.js new file mode 100644 index 0000000000..fe96e8b79e --- /dev/null +++ b/scripts/system/html/js/utils.js @@ -0,0 +1,23 @@ +// +// utils.js +// +// Created by David Back on 19 Nov 2018 +// 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 +// + +function disableDragDrop() { + document.addEventListener("drop", function(event) { + event.preventDefault(); + event.dataTransfer.effectAllowed = "none"; + event.dataTransfer.dropEffect = "none"; + }, false); + + document.addEventListener("dragover", function(event) { + event.preventDefault(); + event.dataTransfer.effectAllowed = "none"; + event.dataTransfer.dropEffect = "none"; + }, false); +} From d55c9467e7b4f6673094567dc16566767cf50262 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 20 Nov 2018 10:37:30 -0800 Subject: [PATCH 090/307] fixing keyboard offset --- .../qml/LoginDialog/CompleteProfileBody.qml | 1 + .../qml/LoginDialog/LinkAccountBody.qml | 1 - .../resources/qml/LoginDialog/SignInBody.qml | 3 +++ .../qml/LoginDialog/UsernameCollisionBody.qml | 6 +++++ .../resources/qml/OverlayLoginDialog.qml | 1 - interface/src/Application.cpp | 24 +++++++++---------- interface/src/Application.h | 2 -- .../scripting/KeyboardScriptingInterface.cpp | 4 ---- .../scripting/KeyboardScriptingInterface.h | 2 -- interface/src/ui/Keyboard.cpp | 15 +++++++++--- interface/src/ui/Keyboard.h | 3 +++ 11 files changed, 36 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index 0b738effc4..8b507269cd 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -252,5 +252,6 @@ Item { root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) } d.resize(); + root.text = ""; } } diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 1e8bcb352a..3643033ba8 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -265,7 +265,6 @@ Item { Component.onCompleted: { d.resize(); - KeyboardScriptingInterface.raised = true; } Component.onDestruction: { if (loginDialog.getLoginDialogPoppedUp() && root.isTablet) { diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index d282937d60..201322e93f 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -90,6 +90,7 @@ Item { emailField.anchors.top = loginContainer.top; emailField.anchors.topMargin = !root.isTablet ? 0.2 * root.height : 0.24 * root.height; cantAccessContainer.anchors.topMargin = !root.isTablet ? 3.5 * hifi.dimensions.contentSpacing.y : hifi.dimensions.contentSpacing.y; + emailField.focus = true; } else { loginButtonAtSignIn.text = "Sign Up"; loginButtonAtSignIn.color = hifi.buttons.blue; @@ -98,6 +99,7 @@ Item { emailField.anchors.top = usernameField.bottom; emailField.anchors.topMargin = 1.5 * hifi.dimensions.contentSpacing.y; passwordField.text = ""; + usernameField.focus = true; } loginContainer.visible = true; } @@ -459,6 +461,7 @@ Item { //but rise Tablet's one instead for Tablet interface root.keyboardEnabled = HMD.active; root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) + root.text = ""; d.resize(); init(loginDialog.isLogIn); } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 2e7429ab99..6db6b38ac8 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -134,6 +134,10 @@ Item { placeholderText: "Choose your own" + onFocusChanged: { + root.text = ""; + } + Keys.onPressed: { if (!visible) { return; @@ -238,7 +242,9 @@ Item { root.keyboardRaised = Qt.binding( function() { return keyboardRaised; }) } + root.text = ""; d.resize(); + textField.focus = true; if (usernameCollisionBody.errorString !== "") { mainTextContainer.visible = true; mainTextContainer.text = usernameCollisionBody.errorString; diff --git a/interface/resources/qml/OverlayLoginDialog.qml b/interface/resources/qml/OverlayLoginDialog.qml index 1e2c4d2c12..0f8420f137 100644 --- a/interface/resources/qml/OverlayLoginDialog.qml +++ b/interface/resources/qml/OverlayLoginDialog.qml @@ -121,7 +121,6 @@ FocusScope { Component.onCompleted: { keyboardTimer.start(); - print(KeyboardScriptingInterface.raised); bodyLoader.setSource("LoginDialog/LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader }); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 47a2670393..b17496ccd0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6722,19 +6722,6 @@ void Application::hmdVisibleChanged(bool visible) { } } -void Application::keyboardRaisedChanged(bool raised) { - auto keyboard = DependencyManager::get().data(); - auto keyboardParentID = getOverlays().getProperty(keyboard->getAnchorID(), "parentID"); - if (raised && !_loginDialogOverlayID.isNull()) { - QVariantMap properties { - { "parentID", _loginDialogOverlayID }, - { "localPosition", vec3toVariant(glm::vec3(-0.3f, -0.3f, 0.2f)) }, - { "localOrientation", quatToVariant(glm::quat(0.0f, 0.0, 1.00f, 0.0f)) } - }; - getOverlays().editOverlay(keyboard->getAnchorID(), properties); - } -} - void Application::updateWindowTitle() const { auto nodeList = DependencyManager::get(); @@ -8670,6 +8657,16 @@ void Application::createLoginDialogOverlay() { }; _loginDialogOverlayID = getOverlays().addOverlay("web3d", overlayProperties); + auto keyboard = DependencyManager::get().data(); + if (!keyboard->getAnchorID().isNull() && !_loginDialogOverlayID.isNull()) { + QVariantMap properties { + { "parentID", _loginDialogOverlayID }, + { "localPosition", vec3toVariant(glm::vec3(-0.3f, -0.3f, 0.2f)) }, + { "localOrientation", quatToVariant(glm::quat(0.0f, 0.0, 1.0f, 0.0f)) } + }; + getOverlays().editOverlay(keyboard->getAnchorID(), properties); + keyboard->setResetKeyboardPositionOnRaise(false); + } if (!_loginStateManager.isSetUp()) { _loginStateManager.setUp(); } @@ -8684,6 +8681,7 @@ void Application::onDismissedLoginDialog() { { "parentID", QUuid() } }; getOverlays().editOverlay(keyboard->getAnchorID(), properties); + keyboard->setResetKeyboardPositionOnRaise(true); // deleting overlay. qDebug() << "Deleting overlay"; getOverlays().deleteOverlay(_loginDialogOverlayID); diff --git a/interface/src/Application.h b/interface/src/Application.h index 39d03f8903..e3379127a0 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -393,8 +393,6 @@ public slots: void hmdVisibleChanged(bool visible); - void keyboardRaisedChanged(bool raised); - #if (PR_BUILD || DEV_BUILD) void sendWrongProtocolVersionsSignature(bool checked) { ::sendWrongProtocolVersionsSignature(checked); } #endif diff --git a/interface/src/scripting/KeyboardScriptingInterface.cpp b/interface/src/scripting/KeyboardScriptingInterface.cpp index bc6453cdc9..b26e1ec378 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.cpp +++ b/interface/src/scripting/KeyboardScriptingInterface.cpp @@ -12,10 +12,6 @@ #include "KeyboardScriptingInterface.h" #include "ui/Keyboard.h" -QUuid KeyboardScriptingInterface::getAnchorID() { - return DependencyManager::get()->getAnchorID(); -} - bool KeyboardScriptingInterface::isRaised() { return DependencyManager::get()->isRaised(); } diff --git a/interface/src/scripting/KeyboardScriptingInterface.h b/interface/src/scripting/KeyboardScriptingInterface.h index 78ef6e6c09..25efc9d2ab 100644 --- a/interface/src/scripting/KeyboardScriptingInterface.h +++ b/interface/src/scripting/KeyboardScriptingInterface.h @@ -29,7 +29,6 @@ */ class KeyboardScriptingInterface : public QObject, public Dependency { Q_OBJECT - Q_PROPERTY(QUuid anchorID READ getAnchorID) Q_PROPERTY(bool raised READ isRaised WRITE setRaised) Q_PROPERTY(bool password READ isPassword WRITE setPassword) @@ -37,7 +36,6 @@ public: Q_INVOKABLE void loadKeyboardFile(const QString& string); private: - QUuid getAnchorID(); bool isRaised(); void setRaised(bool raised); diff --git a/interface/src/ui/Keyboard.cpp b/interface/src/ui/Keyboard.cpp index 7dfa232f6e..4c216d1ce7 100644 --- a/interface/src/ui/Keyboard.cpp +++ b/interface/src/ui/Keyboard.cpp @@ -292,7 +292,6 @@ void Keyboard::setRaised(bool raised) { _capsEnabled = false; _typedCharacters.clear(); }); - qApp->keyboardRaisedChanged(raised); updateTextDisplay(); } @@ -327,8 +326,10 @@ void Keyboard::raiseKeyboardAnchor(bool raise) const { auto anchorOverlay = std::dynamic_pointer_cast(overlays.getOverlay(anchorOverlayID)); if (anchorOverlay) { std::pair keyboardLocation = calculateKeyboardPositionAndOrientation(); - anchorOverlay->setWorldPosition(keyboardLocation.first); - anchorOverlay->setWorldOrientation(keyboardLocation.second); + if (_resetKeyboardPositionOnRaise) { + anchorOverlay->setWorldPosition(keyboardLocation.first); + anchorOverlay->setWorldOrientation(keyboardLocation.second); + } anchorOverlay->setVisible(raise); QVariantMap textDisplayProperties { @@ -412,6 +413,14 @@ void Keyboard::setPassword(bool password) { updateTextDisplay(); } +void Keyboard::setResetKeyboardPositionOnRaise(bool reset) { + if (_resetKeyboardPositionOnRaise != reset) { + withWriteLock([&] { + _resetKeyboardPositionOnRaise = reset; + }); + } +} + void Keyboard::switchToLayer(int layerIndex) { if (layerIndex >= 0 && layerIndex < (int)_keyboardLayers.size()) { Overlays& overlays = qApp->getOverlays(); diff --git a/interface/src/ui/Keyboard.h b/interface/src/ui/Keyboard.h index b8e7346689..78b1981611 100644 --- a/interface/src/ui/Keyboard.h +++ b/interface/src/ui/Keyboard.h @@ -97,6 +97,8 @@ public: bool isPassword() const; void setPassword(bool password); + void setResetKeyboardPositionOnRaise(bool reset); + void loadKeyboardFile(const QString& keyboardFile); QVector getKeysID(); OverlayID getAnchorID(); @@ -136,6 +138,7 @@ private: bool isLayerSwitchTimerFinished(); bool _raised { false }; + bool _resetKeyboardPositionOnRaise { true }; bool _password { false }; bool _capsEnabled { false }; int _layerIndex { 0 }; From 4d5508118433bb9918c969d3983159ec720cc922 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 20 Nov 2018 10:56:34 -0800 Subject: [PATCH 091/307] changing the keyboard password flag appropriately --- interface/resources/qml/LoginDialog/SignInBody.qml | 6 +++--- .../resources/qml/LoginDialog/UsernameCollisionBody.qml | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 201322e93f..fb7d1cf551 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -217,7 +217,7 @@ Item { } onFocusChanged: { root.text = ""; - root.isPassword = false; + root.isPassword = !focus; } } @@ -266,7 +266,7 @@ Item { } onFocusChanged: { root.text = ""; - root.isPassword = false; + root.isPassword = !focus; } } HifiControlsUit.TextField { @@ -286,7 +286,7 @@ Item { onFocusChanged: { root.text = ""; - root.isPassword = true; + root.isPassword = focus; } Item { diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 6db6b38ac8..52756456d5 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -136,6 +136,7 @@ Item { onFocusChanged: { root.text = ""; + root.isPassword = !focus; } Keys.onPressed: { From 44efd18882ee8454a1c9c27d10f659b049a001e9 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Nov 2018 08:38:30 +1300 Subject: [PATCH 092/307] Make Create and Shapes lasers ignore tablet while editing --- .../controllers/controllerDispatcher.js | 33 ++++++++++++++++--- .../controllerModules/inEditMode.js | 17 ---------- .../system/libraries/entitySelectionTool.js | 5 +-- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index b657faefba..a393205b5b 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -12,7 +12,7 @@ LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, - PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE + PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE, HIFI_EDIT_MANIPULATION_CHANNEL */ controllerDispatcherPlugins = {}; @@ -427,9 +427,19 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } }; + this.leftBlacklistTabletIDs = []; + this.rightBlacklistTabletIDs = []; + + this.setLeftBlacklist = function () { + Pointers.setIgnoreItems(_this.leftPointer, _this.blacklist.concat(_this.leftBlacklistTabletIDs)); + }; + this.setRightBlacklist = function () { + Pointers.setIgnoreItems(_this.rightPointer, _this.blacklist.concat(_this.rightBlacklistTabletIDs)); + }; + this.setBlacklist = function() { - Pointers.setIgnoreItems(_this.leftPointer, this.blacklist); - Pointers.setIgnoreItems(_this.rightPointer, this.blacklist); + _this.setLeftBlacklist(); + _this.setRightBlacklist(); }; var MAPPING_NAME = "com.highfidelity.controllerDispatcher"; @@ -493,7 +503,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); filter: Picks.PICK_OVERLAYS | Picks.PICK_ENTITIES | Picks.PICK_INCLUDE_NONCOLLIDABLE, enabled: true }); - this.handleHandMessage = function(channel, data, sender) { + this.handleMessage = function (channel, data, sender) { var message; if (sender === MyAvatar.sessionUUID) { try { @@ -514,6 +524,17 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.setBlacklist(); } } + } else if (channel === HIFI_EDIT_MANIPULATION_CHANNEL) { + message = JSON.parse(data); + var tabletIDs = message.action === "startEdit" ? + [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] : []; + if (message.hand === Controller.Standard.LeftHand) { + _this.leftBlacklistTabletIDs = tabletIDs; + _this.setLeftBlacklist(); + } else if (message.hand === Controller.Standard.RightHand) { + _this.rightBlacklistTabletIDs = tabletIDs; + _this.setRightBlacklist(); + } } } catch (e) { print("WARNING: handControllerGrab.js -- error parsing message: " + data); @@ -554,7 +575,9 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); - Messages.messageReceived.connect(controllerDispatcher.handleHandMessage); + Messages.subscribe(HIFI_EDIT_MANIPULATION_CHANNEL); + Messages.messageReceived.connect(controllerDispatcher.handleMessage); + Script.scriptEnding.connect(controllerDispatcher.cleanup); Script.setTimeout(controllerDispatcher.update, BASIC_TIMER_INTERVAL_MS); }()); diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 5d0b64606d..3a1b835146 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -20,7 +20,6 @@ Script.include("/~/system/libraries/utils.js"); var MARGIN = 25; function InEditMode(hand) { this.hand = hand; - this.running = false; this.triggerClicked = false; this.selectedTarget = null; this.reticleMinX = MARGIN; @@ -114,26 +113,10 @@ Script.include("/~/system/libraries/utils.js"); }; this.runModule = function() { - if (!this.running) { - Messages.sendLocalMessage(this.ENTITY_TOOL_UPDATES_CHANNEL, JSON.stringify({ - method: "moduleRunning", - hand: this.hand, - running: true - })); - this.running = true; - } return makeRunningValues(true, [], []); }; this.exitModule = function() { - if (this.running) { - Messages.sendLocalMessage(this.ENTITY_TOOL_UPDATES_CHANNEL, JSON.stringify({ - method: "moduleRunning", - hand: this.hand, - running: false - })); - this.running = false; - } return makeRunningValues(false, [], []); }; diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 191a961228..4552ed27fb 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -58,10 +58,7 @@ SelectionManager = (function() { return; } - if (messageParsed.method === "moduleRunning") { - // Terminate any current laser or mouse action. - SelectionDisplay.mouseReleaseEvent({}); - } else if (messageParsed.method === "selectEntity") { + if (messageParsed.method === "selectEntity") { if (!SelectionDisplay.triggered() || SelectionDisplay.triggeredHand === messageParsed.hand) { if (wantDebug) { print("setting selection to " + messageParsed.entityID); From c3a0805db1eadb9386297452b2a8692296c77cd3 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 21 Nov 2018 10:28:46 +1300 Subject: [PATCH 093/307] Make far grab ignore tablet while grabbing an entity --- .../farActionGrabEntityDynOnly.js | 14 +++++++++----- .../controllerModules/farParentGrabEntity.js | 17 +++++++++-------- .../controllers/controllerModules/farTrigger.js | 2 +- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index f983ed1e7d..0ba3dd6e6b 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -48,6 +48,7 @@ Script.include("/~/system/libraries/controllers.js"); function FarActionGrabEntity(hand) { this.hand = hand; + this.grabbing = false; this.grabbedThingID = null; this.targetObject = null; this.actionID = null; // action this script created... @@ -151,6 +152,7 @@ Script.include("/~/system/libraries/controllers.js"); Controller.triggerHapticPulse(HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, this.hand); this.previousRoomControllerPosition = roomControllerPosition; + this.grabbing = true; }; this.continueDistanceHolding = function(controllerData) { @@ -246,6 +248,7 @@ Script.include("/~/system/libraries/controllers.js"); this.grabbedThingID = null; this.targetObject = null; this.potentialEntityWithContextOverlay = false; + this.grabbing = false; }; this.updateRecommendedArea = function() { @@ -357,8 +360,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData) || this.targetIsNull()) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.targetIsNull()) { this.endFarGrabAction(); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); @@ -375,10 +377,12 @@ Script.include("/~/system/libraries/controllers.js"); this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar", this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", - this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" + this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity" ]; + if (!this.grabbing) { + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + } var nearGrabReadiness = []; for (var i = 0; i < nearGrabNames.length; i++) { diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index f85869aa7f..c023c4032d 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -47,6 +47,7 @@ Script.include("/~/system/libraries/controllers.js"); function FarParentGrabEntity(hand) { this.hand = hand; + this.grabbing = false; this.targetEntityID = null; this.targetObject = null; this.previouslyUnhooked = {}; @@ -453,8 +454,7 @@ Script.include("/~/system/libraries/controllers.js"); }; this.run = function (controllerData) { - if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || - this.notPointingAtEntity(controllerData) || this.targetIsNull()) { + if (controllerData.triggerValues[this.hand] < TRIGGER_OFF_VALUE || this.targetIsNull()) { this.endFarParentGrab(controllerData); Selection.removeFromSelectedItemsList(DISPATCHER_HOVERING_LIST, "entity", this.highlightedEntity); this.highlightedEntity = null; @@ -470,10 +470,12 @@ Script.include("/~/system/libraries/controllers.js"); this.hand === RIGHT_HAND ? "RightScaleAvatar" : "LeftScaleAvatar", this.hand === RIGHT_HAND ? "RightFarTriggerEntity" : "LeftFarTriggerEntity", this.hand === RIGHT_HAND ? "RightNearActionGrabEntity" : "LeftNearActionGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity", - this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay", - this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight" + this.hand === RIGHT_HAND ? "RightNearParentingGrabEntity" : "LeftNearParentingGrabEntity" ]; + if (!this.grabbing) { + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearParentingGrabOverlay" : "LeftNearParentingGrabOverlay"); + nearGrabNames.push(this.hand === RIGHT_HAND ? "RightNearTabletHighlight" : "LeftNearTabletHighlight"); + } var nearGrabReadiness = []; for (var i = 0; i < nearGrabNames.length; i++) { @@ -483,11 +485,10 @@ Script.include("/~/system/libraries/controllers.js"); } if (this.targetEntityID) { - // if we are doing a distance grab and the object or tablet gets close enough to the controller, + // if we are doing a distance grab and the object gets close enough to the controller, // stop the far-grab so the near-grab or equip can take over. for (var k = 0; k < nearGrabReadiness.length; k++) { - if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID || - HMD.tabletID && nearGrabReadiness[k].targets[0] === HMD.tabletID)) { + if (nearGrabReadiness[k].active && (nearGrabReadiness[k].targets[0] === this.targetEntityID)) { this.endFarParentGrab(controllerData); return makeRunningValues(false, [], []); } diff --git a/scripts/system/controllers/controllerModules/farTrigger.js b/scripts/system/controllers/controllerModules/farTrigger.js index 2b003e4732..c9c9d3deee 100644 --- a/scripts/system/controllers/controllerModules/farTrigger.js +++ b/scripts/system/controllers/controllerModules/farTrigger.js @@ -37,7 +37,7 @@ Script.include("/~/system/libraries/controllers.js"); this.getTargetProps = function (controllerData) { var targetEntity = controllerData.rayPicks[this.hand].objectID; - if (targetEntity) { + if (targetEntity && controllerData.rayPicks[this.hand].type === RayPick.INTERSECTED_ENTITY) { var targetProperties = Entities.getEntityProperties(targetEntity, DISPATCHER_PROPERTIES); if (entityWantsFarTrigger(targetProperties)) { return targetProperties; From b0aa1b2af28bf573ba27c07793f45d3c54512c25 Mon Sep 17 00:00:00 2001 From: birarda Date: Tue, 20 Nov 2018 11:54:57 -0800 Subject: [PATCH 094/307] make domain settings maps keyed case insensitively --- .../resources/web/js/base-settings.js | 8 ++-- domain-server/src/DomainServer.cpp | 38 ++++++++++++------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/domain-server/resources/web/js/base-settings.js b/domain-server/resources/web/js/base-settings.js index fd404aff20..bd96f636a8 100644 --- a/domain-server/resources/web/js/base-settings.js +++ b/domain-server/resources/web/js/base-settings.js @@ -364,7 +364,7 @@ function validateInputs() { if (keyVal.length === 0) { empty = true - markParentRowInvalid(input); + markParentRowInvalid(input) return; } @@ -373,11 +373,13 @@ function validateInputs() { _.each(otherKeys, function(otherKeyCell) { var keyInput = $(otherKeyCell).children('input'); + var lowerNewValue = keyVal.toLowerCase(); + if (keyInput.length) { - if ($(keyInput).val() == keyVal) { + if ($(keyInput).val().toLowerCase() == lowerNewValue) { duplicateKey = true; } - } else if ($(otherKeyCell).html() == keyVal) { + } else if ($(otherKeyCell).html().toLowerCase() == lowerNewValue) { duplicateKey = true; } diff --git a/domain-server/src/DomainServer.cpp b/domain-server/src/DomainServer.cpp index 69f16af8b3..258038b8f1 100644 --- a/domain-server/src/DomainServer.cpp +++ b/domain-server/src/DomainServer.cpp @@ -3172,24 +3172,34 @@ void DomainServer::processPathQueryPacket(QSharedPointer messag const QString PATH_VIEWPOINT_KEY = "viewpoint"; const QString INDEX_PATH = "/"; - // check out paths in the _configMap to see if we have a match - auto keypath = QString(PATHS_SETTINGS_KEYPATH_FORMAT).arg(SETTINGS_PATHS_KEY).arg(pathQuery); - QVariant pathMatch = _settingsManager.valueForKeyPath(keypath); + QString responseViewpoint; - if (pathMatch.isValid() || pathQuery == INDEX_PATH) { + // check out paths in the _configMap to see if we have a match + auto pathsVariant = _settingsManager.valueForKeyPath(SETTINGS_PATHS_KEY); + + auto lowerPathQuery = pathQuery.toLower(); + + if (pathsVariant.canConvert()) { + auto pathsMap = pathsVariant.toMap(); + + // enumerate the paths and look case-insensitively for a matching one + for (auto it = pathsMap.constKeyValueBegin(); it != pathsMap.constKeyValueEnd(); ++it) { + if ((*it).first.toLower() == lowerPathQuery) { + responseViewpoint = (*it).second.toMap()[PATH_VIEWPOINT_KEY].toString().toLower(); + break; + } + } + } + + if (responseViewpoint.isEmpty() && pathQuery == INDEX_PATH) { + const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1"; + responseViewpoint = DEFAULT_INDEX_PATH; + } + + if (!responseViewpoint.isEmpty()) { // we got a match, respond with the resulting viewpoint auto nodeList = DependencyManager::get(); - QString responseViewpoint; - - // if we didn't match the path BUT this is for the index path then send back our default - if (pathMatch.isValid()) { - responseViewpoint = pathMatch.toMap()[PATH_VIEWPOINT_KEY].toString(); - } else { - const QString DEFAULT_INDEX_PATH = "/0,0,0/0,0,0,1"; - responseViewpoint = DEFAULT_INDEX_PATH; - } - if (!responseViewpoint.isEmpty()) { QByteArray viewpointUTF8 = responseViewpoint.toUtf8(); From b2bd0d99de4b05f41c5f66b1f819c713437a0df0 Mon Sep 17 00:00:00 2001 From: birarda Date: Tue, 20 Nov 2018 16:07:48 -0800 Subject: [PATCH 095/307] add a warning for cloud domain ID changes --- .../resources/web/settings/js/settings.js | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/domain-server/resources/web/settings/js/settings.js b/domain-server/resources/web/settings/js/settings.js index 3888277c00..2950b8de75 100644 --- a/domain-server/resources/web/settings/js/settings.js +++ b/domain-server/resources/web/settings/js/settings.js @@ -18,9 +18,6 @@ $(document).ready(function(){ Settings.extraGroupsAtIndex = Settings.extraDomainGroupsAtIndex; Settings.afterReloadActions = function() { - // append the domain selection modal - appendDomainIDButtons(); - // call our method to setup the HF account button setupHFAccountButton(); @@ -52,6 +49,11 @@ $(document).ready(function(){ if (cloudWizardExit != undefined) { $('#cloud-domains-alert').show(); } + + $(Settings.DOMAIN_ID_SELECTOR).siblings('span').append("
    Changing the domain ID for a Cloud Domain may result in an incorrect status for the domain on your Cloud Domains page."); + } else { + // append the domain selection modal + appendDomainIDButtons(); } handleAction(); @@ -59,9 +61,9 @@ $(document).ready(function(){ Settings.handlePostSettings = function(formJSON) { - if (!verifyAvatarHeights()) { - return false; - } + if (!verifyAvatarHeights()) { + return false; + } // check if we've set the basic http password if (formJSON["security"]) { From cdee10d0b85e4e736d3006b4eddf9654357eead7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 20 Nov 2018 16:54:16 -0800 Subject: [PATCH 096/307] getting dominant hand laser + sign up working --- .../qml/LoginDialog/LinkAccountBody.qml | 12 +++------ .../qml/LoginDialog/LoggingInBody.qml | 7 ++--- .../resources/qml/LoginDialog/SignInBody.qml | 22 ++++++++++++++- interface/src/Application.cpp | 9 +++++-- interface/src/LoginStateManager.cpp | 27 ++++++++++++++----- interface/src/LoginStateManager.h | 3 ++- interface/src/ui/LoginDialog.cpp | 8 ++++-- 7 files changed, 65 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 3643033ba8..8f7835195a 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -28,12 +28,6 @@ Item { property bool withSteam: false - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - onKeyboardRaisedChanged: d.resize(); - QtObject { id: d readonly property int minWidth: 480 @@ -55,8 +49,7 @@ Item { parent.width = root.width = newWidth; } - parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) - + (keyboardEnabled && keyboardRaised ? (200 + 2 * hifi.dimensions.contentSpacing.y) : hifi.dimensions.contentSpacing.y); + parent.height = root.height = Math.max(d.minHeight, Math.min(d.maxHeight, targetHeight)) + hifi.dimensions.contentSpacing.y; } } @@ -264,6 +257,9 @@ Item { } Component.onCompleted: { + root.keyboardEnabled = false; + root.keyboardRaised = false; + KeyboardScriptingInterface.raised = false; d.resize(); } Component.onDestruction: { diff --git a/interface/resources/qml/LoginDialog/LoggingInBody.qml b/interface/resources/qml/LoginDialog/LoggingInBody.qml index 6913269c7c..4f7db1c4bf 100644 --- a/interface/resources/qml/LoginDialog/LoggingInBody.qml +++ b/interface/resources/qml/LoginDialog/LoggingInBody.qml @@ -230,7 +230,7 @@ Item { // Alignment horizontalAlignment: Text.AlignHCenter; verticalAlignment: Text.AlignVCenter; - visible: loggingInBody.withSteam; + visible: false; } } } @@ -262,19 +262,20 @@ Item { onHandleLoginFailed: { console.log("Login Failed") + loggingInSpinner.visible = false; var errorString = ""; if (loggingInBody.withSteam && loggingInBody.fromBody === "LinkAccountBody") { loggingInGlyph.visible = false; loggingInText.text = "Your Steam authentication has failed. Please make sure you are logged into Steam and try again." + loggingInText.width = loggingInBody.width; loggingInText.wrapMode = Text.WordWrap; loggingInText.anchors.centerIn = loggingInHeader; - loggingInText.anchors.bottom = loggingInHeader.bottom; steamFailureTimer.start(); } else if (loggingInBody.withSteam) { errorString = "Your Steam authentication has failed. Please make sure you are logged into Steam and try again."; bodyLoader.setSource("UsernameCollisionBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "errorString": errorString }); } else { - errorString = loginDialog.isLogIn ? "Username or password is incorrect." : "Failed to sign up. Please try again."; + errorString = "Username or password is incorrect."; if (loginDialog.isLogIn && loginDialog.isSteamRunning()) { bodyLoader.setSource("CompleteProfileBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader }); } else { diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index fb7d1cf551..55b40fde30 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -65,7 +65,8 @@ Item { if (loginDialog.isLogIn) { loginDialog.login(emailField.text, passwordField.text); } else { - loginDialog.signup(usernameField.text, emailField.text, passwordField.text); + loginDialog.signup(emailField.text, usernameField.text, passwordField.text); + return; } bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": false, "fromBody": "" }); } @@ -484,4 +485,23 @@ Item { break; } } + Connections { + target: loginDialog + onHandleSignupCompleted: { + console.log("Sign Up Completed"); + + loginDialog.login(usernameField.text, passwordField.text); + bodyLoader.setSource("LoggingInBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader, "withSteam": false, "fromBody": "" }); + } + onHandleSignupFailed: { + console.log("Sign Up Failed") + + loginErrorMessage.visible = (errorString !== ""); + if (errorString !== "") { + loginErrorMessage.text = errorString; + errorContainer.anchors.bottom = loginDialog.isLogIn ? emailField.top : usernameField.top; + errorContainer.anchors.left = emailField.left; + } + } + } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ce4308b228..978bb96a1f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1316,6 +1316,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, &Application::activeDisplayPluginChanged, this, &Application::updateSystemTabletMode); connect(this, &Application::activeDisplayPluginChanged, this, [&](){ auto dialogsManager = DependencyManager::get(); + auto keyboard = DependencyManager::get(); if (getLoginDialogPoppedUp()) { if (_firstRun.get()) { // display mode changed. Don't allow auto-switch to work after this session. @@ -1327,6 +1328,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo createLoginDialogOverlay(); } else { // Desktop mode. + QVariantMap properties{ + { "parentID", QUuid() } + }; + getOverlays().editOverlay(keyboard->getAnchorID(), properties); getOverlays().deleteOverlay(_loginDialogOverlayID); _loginDialogOverlayID = OverlayID(); _loginStateManager.tearDown(); @@ -6251,7 +6256,7 @@ void Application::update(float deltaTime) { _overlays.update(deltaTime); } if (!_loginDialogOverlayID.isNull()) { - _loginStateManager.update(); + _loginStateManager.update(getMyAvatar()->getDominantHand()); } if (!_loginDialogOverlayID.isNull()) { @@ -8662,7 +8667,7 @@ void Application::createLoginDialogOverlay() { QVariantMap properties { { "parentID", _loginDialogOverlayID }, { "localPosition", vec3toVariant(glm::vec3(-0.3f, -0.3f, 0.2f)) }, - { "localOrientation", quatToVariant(glm::quat(0.0f, 0.0, 1.0f, 0.0f)) } + { "localOrientation", quatToVariant(glm::quat(0.0f, 0.0, 1.0f, 0.25f)) }, }; getOverlays().editOverlay(keyboard->getAnchorID(), properties); keyboard->setResetKeyboardPositionOnRaise(false); diff --git a/interface/src/LoginStateManager.cpp b/interface/src/LoginStateManager.cpp index a393cbf178..a77b61e723 100644 --- a/interface/src/LoginStateManager.cpp +++ b/interface/src/LoginStateManager.cpp @@ -19,6 +19,7 @@ #include "controllers/StateController.h" #include "controllers/UserInputMapper.h" #include "raypick/PointerScriptingInterface.h" +#include "raypick/RayPickScriptingInterface.h" #include "raypick/PickScriptingInterface.h" #include "scripting/ControllerScriptingInterface.h" @@ -209,15 +210,29 @@ void LoginStateManager::setUp() { pointers->enablePointer(_rightLoginPointerID); } -void LoginStateManager::update() { +void LoginStateManager::update(const QString dominantHand) { + if (!isSetUp()) { + return; + } + if (_dominantHand != dominantHand) { + _dominantHand = dominantHand; + } auto pointers = DependencyManager::get(); - if (pointers) { + auto raypicks = DependencyManager::get(); + if (pointers && raypicks) { QString mode = "full"; - - if (_leftLoginPointerID > PointerEvent::INVALID_POINTER_ID) { + auto rightObjectID = raypicks->getPrevRayPickResult(_rightLoginPointerID)["objectID"].toUuid(); + auto leftObjectID = raypicks->getPrevRayPickResult(_leftLoginPointerID)["objectID"].toUuid(); + if (_dominantHand == "left" && !leftObjectID.isNull()) { + // dominant is left. + pointers->setRenderState(_rightLoginPointerID, ""); + pointers->setRenderState(_leftLoginPointerID, mode); + } else if (_dominantHand == "right" && !rightObjectID.isNull()) { + // dominant is right. + pointers->setRenderState(_leftLoginPointerID, ""); + pointers->setRenderState(_rightLoginPointerID, mode); + } else { pointers->setRenderState(_leftLoginPointerID, mode); - } - if (_rightLoginPointerID > PointerEvent::INVALID_POINTER_ID) { pointers->setRenderState(_rightLoginPointerID, mode); } } diff --git a/interface/src/LoginStateManager.h b/interface/src/LoginStateManager.h index 8a4c12fb34..3228d2e8e6 100644 --- a/interface/src/LoginStateManager.h +++ b/interface/src/LoginStateManager.h @@ -26,11 +26,12 @@ public: void setUp(); void tearDown(); - void update(); + void update(const QString dominantHand); bool isSetUp() const { return (_leftLoginPointerID > PointerEvent::INVALID_POINTER_ID) && (_rightLoginPointerID > PointerEvent::INVALID_POINTER_ID); } private: + QString _dominantHand; QList _renderStates {}; QList _defaultRenderStates {}; unsigned int _leftLoginPointerID { PointerEvent::INVALID_POINTER_ID }; diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 5b920b90f7..d8339571b7 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -196,13 +196,17 @@ void LoginDialog::openUrl(const QString& url) const { offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) { newObject->setProperty("url", url); }); - LoginDialog::hide(); + if (!qApp->getLoginDialogPoppedUp()) { + LoginDialog::hide(); + } } else { if (!hmd->getShouldShowTablet() && !qApp->isHMDMode()) { offscreenUi->load("Browser.qml", [=](QQmlContext* context, QObject* newObject) { newObject->setProperty("url", url); }); - LoginDialog::hide(); + if (!qApp->getLoginDialogPoppedUp()) { + LoginDialog::hide(); + } } else { tablet->gotoWebScreen(url); } From 572cdcd008f4758debd3c80ea8abb79fcd27c9b3 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 20 Nov 2018 17:09:37 -0800 Subject: [PATCH 097/307] remapping is now able to work remains to clean up code and check for gotchas from old fst code --- libraries/animation/src/AnimSkeleton.cpp | 102 +++++++++++++++--- libraries/animation/src/AnimSkeleton.h | 3 + libraries/animation/src/Rig.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 4 +- .../src/avatars-renderer/SkeletonModel.cpp | 1 + libraries/fbx/src/FBXReader.cpp | 28 ++--- 6 files changed, 110 insertions(+), 30 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 908b4eb1d3..6e71ef0184 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -54,6 +54,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { } _clusterBindMatrixOriginalValues.push_back(dummyClustersList); } + //dump(false); } AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap jointOffsets) { @@ -64,16 +65,18 @@ int AnimSkeleton::nameToJointIndex(const QString& jointName) const { auto itr = _jointIndicesByName.find(jointName); - if (getFBXToHifiJointNameMapping().contains(jointName)) { - qCDebug(animation) << "failing joint name is " << jointName; + if (_fbxToHifiJointNameMapping.contains(jointName)) { + //qCDebug(animation) << "failing joint name is " << jointName; itr = _jointIndicesByName.find(_fbxToHifiJointNameMapping[jointName]); - qCDebug(animation) << "the alternate name for the joint " << jointName << " is " << - _fbxToHifiJointNameMapping[jointName] << " " << itr.value(); + //qCDebug(animation) << "the alternate name for the joint " << jointName << " is " << + //_fbxToHifiJointNameMapping[jointName] << " " << itr.value(); } if (_jointIndicesByName.end() != itr) { + //qCDebug(animation) << "returning " << itr.value() << " for " << jointName; return itr.value(); } + //qCDebug(animation) << "returning -1 " << " for " << jointName; return -1; } @@ -142,6 +145,7 @@ const QString AnimSkeleton::getJointName(int jointIndex) const { break; } } + //qCDebug(animation) << "reverse lookup: returning " << jointName << " for " << jointIndex; return jointName; //;_joints[jointIndex].name; } @@ -215,6 +219,81 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { } } +bool AnimSkeleton::checkNonMirrored(QString jointName) const { + + //bool isNonMirrored = false; + QMapIterator i(_fbxToHifiJointNameMapping); + while (i.hasNext()) { + i.next(); + if (i.value() == jointName) { + // check for left right in the key + if (i.key() != "Hips" && i.key() != "Spine" && + i.key() != "Spine1" && i.key() != "Spine2" && + i.key() != "Neck" && i.key() != "Head" && + !((i.key().startsWith("Left") || i.key().startsWith("Right")) && + i.key() != "LeftEye" && i.key() != "RightEye")) { + //return true + return true; + } else { + return false; + } + } + } + // check the unmapped name + if (jointName != "Hips" && jointName != "Spine" && + jointName != "Spine1" && jointName != "Spine2" && + jointName != "Neck" && jointName != "Head" && + !((jointName.startsWith("Left") || jointName.startsWith("Right")) && + jointName != "LeftEye" && jointName != "RightEye")) { + //return true + return true; + } else { + return false; + } + +} + +int AnimSkeleton::containsLeft(QString jointName) const { + QMapIterator i(_fbxToHifiJointNameMapping); + int mirrorJointIndex = -1; + while (i.hasNext()) { + i.next(); + if (i.value() == jointName) { + // check for left right in the key + if (i.key().startsWith("Left")) { + QString mirrorJointName = QString(i.key()).replace(0, 4, "Right"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + //return true + } + } + } + if (jointName.startsWith("Left")) { + QString mirrorJointName = QString(i.key()).replace(0, 4, "Right"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + return mirrorJointIndex; +} + +int AnimSkeleton::containsRight(QString jointName) const { + QMapIterator i(_fbxToHifiJointNameMapping); + int mirrorJointIndex = -1; + while (i.hasNext()) { + i.next(); + if (i.value() == jointName) { + // check for left right in the key + if (i.key().startsWith("Right")) { + QString mirrorJointName = QString(i.key()).replace(0, 5, "Left"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + } + } + if (jointName.startsWith("Right")) { + QString mirrorJointName = QString(i.key()).replace(0, 5, "Left"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + return mirrorJointIndex; +} + void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, const QMap jointOffsets) { _joints = joints; @@ -266,22 +345,15 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, _nonMirroredIndices.clear(); _mirrorMap.reserve(_jointsSize); for (int i = 0; i < _jointsSize; i++) { - if (_joints[i].name != "Hips" && _joints[i].name != _fbxToHifiJointNameMapping["Hips"] && _joints[i].name != "Spine" && _joints[i].name != _fbxToHifiJointNameMapping["Spine"] && - _joints[i].name != "Spine1" && _joints[i].name != _fbxToHifiJointNameMapping["Spine1"] && _joints[i].name != "Spine2" && _joints[i].name != _fbxToHifiJointNameMapping["Spine2"] && - _joints[i].name != "Neck" && _joints[i].name != _fbxToHifiJointNameMapping["Neck"] && _joints[i].name != "Head" && _joints[i].name != _fbxToHifiJointNameMapping["Head"] && - !((_joints[i].name.startsWith("Left") || _joints[i].name.startsWith("Right")) && - _joints[i].name != "LeftEye" && _joints[i].name != "RightEye")) { + if (checkNonMirrored(_joints[i].name)) { // HACK: we don't want to mirror some joints so we remember their indices // so we can restore them after a future mirror operation _nonMirroredIndices.push_back(i); } int mirrorJointIndex = -1; - if (_joints[i].name.startsWith("Left")) { - QString mirrorJointName = QString(_joints[i].name).replace(0, 4, "Right"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); - } else if (_joints[i].name.startsWith("Right")) { - QString mirrorJointName = QString(_joints[i].name).replace(0, 5, "Left"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); + mirrorJointIndex = containsLeft(_joints[i].name); + if (mirrorJointIndex > -1) { + mirrorJointIndex = containsRight(_joints[i].name); } if (mirrorJointIndex >= 0) { _mirrorMap.push_back(mirrorJointIndex); diff --git a/libraries/animation/src/AnimSkeleton.h b/libraries/animation/src/AnimSkeleton.h index bcbfe628c7..5581976d74 100644 --- a/libraries/animation/src/AnimSkeleton.h +++ b/libraries/animation/src/AnimSkeleton.h @@ -56,6 +56,9 @@ public: void saveNonMirroredPoses(const AnimPoseVec& poses) const; void restoreNonMirroredPoses(AnimPoseVec& poses) const; + bool checkNonMirrored(QString jointName) const; + int containsLeft(QString jointName) const; + int containsRight(QString jointName) const; void mirrorRelativePoses(AnimPoseVec& poses) const; void mirrorAbsolutePoses(AnimPoseVec& poses) const; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 0c30a7696f..1675ea8e90 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -423,7 +423,7 @@ int Rig::indexOfJoint(const QString& jointName) const { int result = _animSkeleton->nameToJointIndex(jointName); if (_animSkeleton->getFBXToHifiJointNameMapping().contains(jointName)) { - qCDebug(animation) << "the alternate name for the joint " << jointName << " is " << _animSkeleton->getFBXToHifiJointNameMapping()[jointName] << " " << _animSkeleton->nameToJointIndex(_animSkeleton->getFBXToHifiJointNameMapping()[jointName]); + //qCDebug(animation) << "the alternate name for the joint " << jointName << " is " << _animSkeleton->getFBXToHifiJointNameMapping()[jointName] << " " << _animSkeleton->nameToJointIndex(_animSkeleton->getFBXToHifiJointNameMapping()[jointName]); result = _animSkeleton->nameToJointIndex(jointName); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index e5435a9a8c..14e97a5bf9 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1446,12 +1446,12 @@ int Avatar::getJointIndex(const QString& name) const { } } else { // doesn't contain name. - qCDebug(avatars_renderer) << "name is not here"; + //qCDebug(avatars_renderer) << "name is not here"; if (_skeletonModel && _skeletonModel->isActive()) { if (_modelJointIndicesCache.contains(_skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name])) { result = _modelJointIndicesCache[_skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name]] - 1; - qCDebug(avatars_renderer) << "joint " << name << " remapped to " << _skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name] << result; + // qCDebug(avatars_renderer) << "joint " << name << " remapped to " << _skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name] << result; } } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 36e37dd3d4..a97ba4bf4b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -317,6 +317,7 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco } glm::vec3 SkeletonModel::getDefaultEyeModelPosition() const { + //qCDebug(avatars_renderer) << "default eye model position " << _defaultEyeModelPosition; return _owningAvatar->getModelScale() * _defaultEyeModelPosition; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 2e43573ec2..3ddcd75255 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -485,7 +485,7 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& QString jointNeckName = processID(getString(joints.value("jointNeck", "jointNeck"))); QString jointRootName = processID(getString(joints.value("jointRoot", "jointRoot"))); QString jointLeanName = processID(getString(joints.value("jointLean", "jointLean"))); - QString jointHeadName = processID(getString(joints.value("jointHead", "jointHead"))); + QString jointHeadName = "Head";// processID(getString(joints.value("jointHead", "jointHead"))); QString jointLeftHandName = processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); QString jointRightHandName = processID(getString(joints.value("jointRightHand", "jointRightHand"))); QString jointEyeLeftID; @@ -534,6 +534,8 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& HFMModel& hfmModel = *hfmModelPtr; hfmModel.originalURL = url; + hfmModel.fbxToHifiJointNameMapping.clear(); + hfmModel.fbxToHifiJointNameMapping = getJointNameMapping(mapping); float unitScaleFactor = 1.0f; glm::vec3 ambientColor; @@ -602,34 +604,34 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& hifiGlobalNodeID = id; } - if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye") { + if (name == jointEyeLeftName || name == "EyeL" || name == "joint_Leye" || (hfmModel.fbxToHifiJointNameMapping.contains(jointEyeLeftName) && (name == hfmModel.fbxToHifiJointNameMapping[jointEyeLeftName]))) { jointEyeLeftID = getID(object.properties); - } else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye") { + } else if (name == jointEyeRightName || name == "EyeR" || name == "joint_Reye" || (hfmModel.fbxToHifiJointNameMapping.contains(jointEyeRightName) && (name == hfmModel.fbxToHifiJointNameMapping[jointEyeRightName]))) { jointEyeRightID = getID(object.properties); - } else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck") { + } else if (name == jointNeckName || name == "NeckRot" || name == "joint_neck" || (hfmModel.fbxToHifiJointNameMapping.contains(jointNeckName) && (name == hfmModel.fbxToHifiJointNameMapping[jointNeckName]))) { jointNeckID = getID(object.properties); - } else if (name == jointRootName) { + } else if (name == jointRootName || (hfmModel.fbxToHifiJointNameMapping.contains(jointRootName) && (name == hfmModel.fbxToHifiJointNameMapping[jointRootName]))) { jointRootID = getID(object.properties); } else if (name == jointLeanName) { jointLeanID = getID(object.properties); - } else if (name == jointHeadName) { + } else if ((name == jointHeadName) || (hfmModel.fbxToHifiJointNameMapping.contains(jointHeadName) && (name == hfmModel.fbxToHifiJointNameMapping[jointHeadName]))) { jointHeadID = getID(object.properties); - } else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand") { + } else if (name == jointLeftHandName || name == "LeftHand" || name == "joint_L_hand" || (hfmModel.fbxToHifiJointNameMapping.contains(jointLeftHandName) && (name == hfmModel.fbxToHifiJointNameMapping[jointLeftHandName]))) { jointLeftHandID = getID(object.properties); - } else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand") { + } else if (name == jointRightHandName || name == "RightHand" || name == "joint_R_hand" || (hfmModel.fbxToHifiJointNameMapping.contains(jointRightHandName) && (name == hfmModel.fbxToHifiJointNameMapping[jointRightHandName]))) { jointRightHandID = getID(object.properties); - } else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End") { + } else if (name == "LeftToe" || name == "joint_L_toe" || name == "LeftToe_End" || (hfmModel.fbxToHifiJointNameMapping.contains("LeftToe") && (name == hfmModel.fbxToHifiJointNameMapping["LeftToe"]))) { jointLeftToeID = getID(object.properties); - } else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End") { + } else if (name == "RightToe" || name == "joint_R_toe" || name == "RightToe_End" || (hfmModel.fbxToHifiJointNameMapping.contains("RightToe") && (name == hfmModel.fbxToHifiJointNameMapping["RightToe"]))) { jointRightToeID = getID(object.properties); } @@ -1839,14 +1841,16 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& QString jointName = itr.key(); glm::quat rotationOffset = itr.value(); int jointIndex = hfmModel.getJointIndex(jointName); + if (hfmModel.fbxToHifiJointNameMapping.contains(jointName)) { + jointIndex = hfmModel.getJointIndex(hfmModel.fbxToHifiJointNameMapping[jointName]); + } if (jointIndex != -1) { hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - hfmModel.fbxToHifiJointNameMapping.clear(); - hfmModel.fbxToHifiJointNameMapping = getJointNameMapping(mapping); + return hfmModelPtr; } From a4883649760c15a6ff148720e3f49874c6277c6f Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 20 Nov 2018 17:14:35 -0800 Subject: [PATCH 098/307] fixed mirroring helper functions contains left and contains right --- libraries/animation/src/AnimSkeleton.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 6e71ef0184..e928658366 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -268,7 +268,7 @@ int AnimSkeleton::containsLeft(QString jointName) const { } } if (jointName.startsWith("Left")) { - QString mirrorJointName = QString(i.key()).replace(0, 4, "Right"); + QString mirrorJointName = QString(jointName).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } return mirrorJointIndex; @@ -288,7 +288,7 @@ int AnimSkeleton::containsRight(QString jointName) const { } } if (jointName.startsWith("Right")) { - QString mirrorJointName = QString(i.key()).replace(0, 5, "Left"); + QString mirrorJointName = QString(jointName).replace(0, 5, "Left"); mirrorJointIndex = nameToJointIndex(mirrorJointName); } return mirrorJointIndex; From d12655ed7b6702c93d938f300af9634ad51cdf79 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 21 Nov 2018 12:21:19 -0800 Subject: [PATCH 099/307] sfx to login mousearea/hiding desktop toolbar --- .../qml/LoginDialog/CompleteProfileBody.qml | 8 ++- .../qml/LoginDialog/LinkAccountBody.qml | 6 ++ .../resources/qml/LoginDialog/SignInBody.qml | 7 ++ .../qml/LoginDialog/UsernameCollisionBody.qml | 6 ++ .../qml/dialogs/TabletLoginDialog.qml | 5 +- interface/src/Application.cpp | 64 ++++++++++--------- interface/src/Application.h | 1 + 7 files changed, 60 insertions(+), 37 deletions(-) diff --git a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml index 8b507269cd..6363a6b608 100644 --- a/interface/resources/qml/LoginDialog/CompleteProfileBody.qml +++ b/interface/resources/qml/LoginDialog/CompleteProfileBody.qml @@ -14,6 +14,7 @@ import QtQuick.Controls.Styles 1.4 as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 Item { id: completeProfileBody @@ -150,11 +151,14 @@ Item { MouseArea { id: cancelArea anchors.fill: parent - acceptedButtons: Qt.LeftButton + hoverEnabled: true + onEntered: { + Tablet.playSound(TabletEnums.ButtonHover); + } onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader }); } - } } TextMetrics { id: profileButtonTextMetrics diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 8f7835195a..51179cf9d8 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -15,6 +15,7 @@ import QtQuick.Controls.Styles 1.4 as OriginalStyles import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 Item { id: linkAccountBody @@ -240,7 +241,12 @@ Item { id: dismissMouseArea anchors.fill: parent acceptedButtons: Qt.LeftButton + hoverEnabled: true + onEntered: { + Tablet.playSound(TabletEnums.ButtonHover); + } onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); if (loginDialog.getLoginDialogPoppedUp()) { console.log("[ENCOURAGELOGINDIALOG]: user dismissed login screen") var data = { diff --git a/interface/resources/qml/LoginDialog/SignInBody.qml b/interface/resources/qml/LoginDialog/SignInBody.qml index 55b40fde30..d48615cfbd 100644 --- a/interface/resources/qml/LoginDialog/SignInBody.qml +++ b/interface/resources/qml/LoginDialog/SignInBody.qml @@ -409,7 +409,14 @@ Item { id: cancelArea anchors.fill: parent acceptedButtons: Qt.LeftButton + hoverEnabled: true + + onEntered: { + Tablet.playSound(TabletEnums.ButtonHover); + } + onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader }); } } diff --git a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml index 52756456d5..2fa43b0ffa 100644 --- a/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml +++ b/interface/resources/qml/LoginDialog/UsernameCollisionBody.qml @@ -14,6 +14,7 @@ import QtQuick.Controls 1.4 import controlsUit 1.0 as HifiControlsUit import stylesUit 1.0 as HifiStylesUit +import TabletScriptingInterface 1.0 Item { id: usernameCollisionBody @@ -202,7 +203,12 @@ Item { id: cancelArea anchors.fill: parent acceptedButtons: Qt.LeftButton + hoverEnabled: true + onEntered: { + Tablet.playSound(TabletEnums.ButtonHover); + } onClicked: { + Tablet.playSound(TabletEnums.ButtonClick); bodyLoader.setSource("LinkAccountBody.qml", { "loginDialog": loginDialog, "root": root, "bodyLoader": bodyLoader }); } } diff --git a/interface/resources/qml/dialogs/TabletLoginDialog.qml b/interface/resources/qml/dialogs/TabletLoginDialog.qml index 34000fdec2..23ed4886b3 100644 --- a/interface/resources/qml/dialogs/TabletLoginDialog.qml +++ b/interface/resources/qml/dialogs/TabletLoginDialog.qml @@ -87,10 +87,6 @@ FocusScope { id: bodyLoader anchors.fill: parent } - - Component.onDestruction: { - loginKeyboard.raised = false; - KeyboardScriptingInterface.raised = false; } Image { @@ -143,6 +139,7 @@ FocusScope { Component.onDestruction: { loginKeyboard.raised = false; + KeyboardScriptingInterface.raised = false; } Component.onCompleted: { diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 978bb96a1f..ae7d4c3cca 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1322,12 +1322,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // display mode changed. Don't allow auto-switch to work after this session. _firstRun.set(false); } - if (_loginDialogOverlayID.isNull()) { - // HMD mode. + if (isHMDMode()) { dialogsManager->hideLoginDialog(); createLoginDialogOverlay(); } else { - // Desktop mode. QVariantMap properties{ { "parentID", QUuid() } }; @@ -1335,6 +1333,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo getOverlays().deleteOverlay(_loginDialogOverlayID); _loginDialogOverlayID = OverlayID(); _loginStateManager.tearDown(); + auto toolbar = DependencyManager::get()->getToolbar("com.highfidelity.interface.toolbar.system"); + toolbar->writeProperty("visible", false); dialogsManager->showLoginDialog(); } } @@ -2946,6 +2946,10 @@ void Application::showLoginScreen() { auto accountManager = DependencyManager::get(); auto dialogsManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { + if (!isHMDMode()) { + auto toolbar = DependencyManager::get()->getToolbar("com.highfidelity.interface.toolbar.system"); + toolbar->writeProperty("visible", false); + } _loginDialogPoppedUp = true; dialogsManager->showLoginDialog(); QJsonObject loginData = {}; @@ -5302,6 +5306,8 @@ void Application::resumeAfterLoginDialogActionTaken() { return; } + updateSystemTabletMode(); + getMyAvatar()->setEnableMeshVisible(true); const auto& nodeList = DependencyManager::get(); @@ -6257,33 +6263,7 @@ void Application::update(float deltaTime) { } if (!_loginDialogOverlayID.isNull()) { _loginStateManager.update(getMyAvatar()->getDominantHand()); - } - - if (!_loginDialogOverlayID.isNull()) { - auto overlayPosition = getOverlays().getProperty(_loginDialogOverlayID, "position"); - auto overlayPositionVec = vec3FromVariant(overlayPosition.value); - //auto avatarHeadPosition = myAvatar->getHeadPosition(); - //auto avatarHeadOrientation = myAvatar->getHeadOrientation(); - //auto headLookVec = avatarHeadOrientation * Vectors::FRONT; - //auto overlayToHeadVec = positionVec - avatarHeadPosition; - auto cameraPositionVec = _myCamera.getPosition(); - auto cameraOrientation = _myCamera.getOrientation(); - auto headLookVec = (cameraOrientation * Vectors::FRONT); - auto overlayToHeadVec = overlayPositionVec - cameraPositionVec; - //auto lookAtQuat = glm::inverse(glm::quat_cast(glm::lookAt(positionVec, avatarHeadPosition, avatarHeadOrientation * Vectors::UNIT_Y))); - auto pointAngle = (glm::acos(glm::dot(glm::normalize(overlayToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); - auto upVec = myAvatar->getWorldOrientation() * Vectors::UNIT_Y; - auto offset = headLookVec * 0.7f; - auto newOverlayPositionVec = (cameraPositionVec + offset) + (upVec * -0.1f); - auto newOverlayOrientation = glm::inverse(glm::quat_cast(glm::lookAt(newOverlayPositionVec, cameraPositionVec, upVec))) * Quaternions::Y_180; - - if (pointAngle > 30.0f) { - QVariantMap properties { - {"position", vec3toVariant(newOverlayPositionVec)}, - {"orientation", quatToVariant(newOverlayOrientation)} - }; - getOverlays().editOverlay(_loginDialogOverlayID, properties); - } + updateLoginDialogOverlayPosition(); } // Update _viewFrustum with latest camera and view frustum data... @@ -8677,6 +8657,28 @@ void Application::createLoginDialogOverlay() { } } +void Application::updateLoginDialogOverlayPosition() { + auto overlayPosition = getOverlays().getProperty(_loginDialogOverlayID, "position"); + auto overlayPositionVec = vec3FromVariant(overlayPosition.value); + auto cameraPositionVec = _myCamera.getPosition(); + auto cameraOrientation = _myCamera.getOrientation(); + auto headLookVec = (cameraOrientation * Vectors::FRONT); + auto overlayToHeadVec = overlayPositionVec - cameraPositionVec; + auto pointAngle = (glm::acos(glm::dot(glm::normalize(overlayToHeadVec), glm::normalize(headLookVec))) * 180.0f / PI); + auto upVec = getMyAvatar()->getWorldOrientation() * Vectors::UNIT_Y; + auto offset = headLookVec * 0.7f; + auto newOverlayPositionVec = (cameraPositionVec + offset) + (upVec * -0.1f); + auto newOverlayOrientation = glm::inverse(glm::quat_cast(glm::lookAt(newOverlayPositionVec, cameraPositionVec, upVec))) * Quaternions::Y_180; + + if (pointAngle > 30.0f) { + QVariantMap properties { + {"position", vec3toVariant(newOverlayPositionVec)}, + {"orientation", quatToVariant(newOverlayOrientation)} + }; + getOverlays().editOverlay(_loginDialogOverlayID, properties); + } +} + void Application::onDismissedLoginDialog() { _loginDialogPoppedUp = false; loginDialogPoppedUp.set(false); @@ -8846,7 +8848,7 @@ void Application::updateThreadPoolCount() const { } void Application::updateSystemTabletMode() { - if (_settingsLoaded) { + if (_settingsLoaded && !getLoginDialogPoppedUp()) { qApp->setProperty(hifi::properties::HMD, isHMDMode()); if (isHMDMode()) { DependencyManager::get()->setToolbarMode(getHmdTabletBecomesToolbarSetting()); diff --git a/interface/src/Application.h b/interface/src/Application.h index e3379127a0..573919cd26 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -324,6 +324,7 @@ public: bool getLoginDialogPoppedUp() const { return _loginDialogPoppedUp; } void createLoginDialogOverlay(); + void updateLoginDialogOverlayPosition(); #if defined(Q_OS_ANDROID) void beforeEnterBackground(); From 51f5942a7f832e685208fb05e93fd567c6a72845 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 21 Nov 2018 13:36:36 -0800 Subject: [PATCH 100/307] adding sound to login screen --- .../resources}/sounds/crystals_and_voices.mp3 | Bin interface/src/Application.cpp | 30 ++++++++++++++++-- interface/src/Application.h | 2 ++ scripts/system/interstitialPage.js | 2 +- 4 files changed, 30 insertions(+), 4 deletions(-) rename {scripts/system/assets => interface/resources}/sounds/crystals_and_voices.mp3 (100%) diff --git a/scripts/system/assets/sounds/crystals_and_voices.mp3 b/interface/resources/sounds/crystals_and_voices.mp3 similarity index 100% rename from scripts/system/assets/sounds/crystals_and_voices.mp3 rename to interface/resources/sounds/crystals_and_voices.mp3 diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index ae7d4c3cca..cb34ea8eee 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1033,8 +1033,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _maxOctreePPS(maxOctreePacketsPerSecond.get()), _lastFaceTrackerUpdate(0), _snapshotSound(nullptr), - _sampleSound(nullptr) - + _sampleSound(nullptr), + _loginStateSound(nullptr) { auto steamClient = PluginManager::getInstance()->getSteamClientPlugin(); @@ -2286,6 +2286,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); _snapshotSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/snapshot/snap.wav")); + _loginStateSound = DependencyManager::get()->getSound(PathUtils::resourcesUrl("sounds/crystals_and_voices.mp3")); // Monitor model assets (e.g., from Clara.io) added to the world that may need resizing. static const int ADD_ASSET_TO_WORLD_TIMER_INTERVAL_MS = 1000; @@ -2634,6 +2635,10 @@ void Application::cleanupBeforeQuit() { _snapshotSoundInjector->stop(); } + if (_loginStateSoundInjector != nullptr) { + _loginStateSoundInjector->stop(); + } + // destroy Audio so it and its threads have a chance to go down safely // this must happen after QML, as there are unexplained audio crashes originating in qtwebengine DependencyManager::destroy(); @@ -2945,6 +2950,12 @@ static void addDisplayPluginToMenu(const DisplayPluginPointer& displayPlugin, in void Application::showLoginScreen() { auto accountManager = DependencyManager::get(); auto dialogsManager = DependencyManager::get(); + if (!_loginStateSound->isReady()) { + connect(_loginStateSound.data(), &Sound::ready, this, &Application::showLoginScreen); + return; + } else { + disconnect(_loginStateSound.data(), &Sound::ready, this, &Application::showLoginScreen); + } if (!accountManager->isLoggedIn()) { if (!isHMDMode()) { auto toolbar = DependencyManager::get()->getToolbar("com.highfidelity.interface.toolbar.system"); @@ -2956,6 +2967,15 @@ void Application::showLoginScreen() { loginData["action"] = "login dialog shown"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); _window->setWindowTitle("High Fidelity Interface"); + if (!_loginStateSoundInjector) { + AudioInjectorOptions options; + options.localOnly = true; + options.position = getMyAvatar()->getHeadPosition(); + options.loop = true; + options.volume = 0.4f; + options.stereo = true; + _loginStateSoundInjector = AudioInjector::playSound(_loginStateSound, options); + } } else { resumeAfterLoginDialogActionTaken(); } @@ -5298,6 +5318,7 @@ void Application::pauseUntilLoginDetermined() { // disconnect domain handler. nodeList->getDomainHandler().disconnect(); + } void Application::resumeAfterLoginDialogActionTaken() { @@ -8670,7 +8691,7 @@ void Application::updateLoginDialogOverlayPosition() { auto newOverlayPositionVec = (cameraPositionVec + offset) + (upVec * -0.1f); auto newOverlayOrientation = glm::inverse(glm::quat_cast(glm::lookAt(newOverlayPositionVec, cameraPositionVec, upVec))) * Quaternions::Y_180; - if (pointAngle > 30.0f) { + if (pointAngle > 40.0f) { QVariantMap properties { {"position", vec3toVariant(newOverlayPositionVec)}, {"orientation", quatToVariant(newOverlayOrientation)} @@ -8695,6 +8716,9 @@ void Application::onDismissedLoginDialog() { _loginDialogOverlayID = OverlayID(); _loginStateManager.tearDown(); } + if (_loginStateSoundInjector != nullptr) { + _loginStateSoundInjector->stop(); + } resumeAfterLoginDialogActionTaken(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index 573919cd26..1e9f30dfc1 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -794,6 +794,8 @@ private: AudioInjectorPointer _snapshotSoundInjector; SharedSoundPointer _snapshotSound; SharedSoundPointer _sampleSound; + AudioInjectorPointer _loginStateSoundInjector; + SharedSoundPointer _loginStateSound; DisplayPluginPointer _autoSwitchDisplayModeSupportedHMDPlugin; QString _autoSwitchDisplayModeSupportedHMDPluginName; diff --git a/scripts/system/interstitialPage.js b/scripts/system/interstitialPage.js index e4dc778985..8dd94623b7 100644 --- a/scripts/system/interstitialPage.js +++ b/scripts/system/interstitialPage.js @@ -21,7 +21,7 @@ var TEXTURE_EPSILON = 0.01; var isVisible = false; var VOLUME = 0.4; - var tune = SoundCache.getSound(Script.resolvePath("/~/system/assets/sounds/crystals_and_voices.mp3")); + var tune = SoundCache.getSound(Script.resourcesPath() + "sounds/crystals_and_voices.mp3"); var sample = null; var MAX_LEFT_MARGIN = 1.9; var INNER_CIRCLE_WIDTH = 4.7; From 5e51ba051ab5dbdc13b063eff0735a4c2a63772f Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 22 Nov 2018 11:12:22 +1300 Subject: [PATCH 101/307] Improve code responsibilities --- .../controllers/controllerDispatcher.js | 24 +++---- .../controllerModules/inEditMode.js | 32 +++++++++ .../controllerModules/inVREditMode.js | 65 ++++++++++++------- .../nearParentGrabOverlay.js | 43 +++++------- .../controllerModules/nearTabletHighlight.js | 43 +++++------- .../libraries/controllerDispatcherUtils.js | 4 -- .../system/libraries/entitySelectionTool.js | 20 +++--- 7 files changed, 127 insertions(+), 104 deletions(-) diff --git a/scripts/system/controllers/controllerDispatcher.js b/scripts/system/controllers/controllerDispatcher.js index a393205b5b..a880eb8b10 100644 --- a/scripts/system/controllers/controllerDispatcher.js +++ b/scripts/system/controllers/controllerDispatcher.js @@ -12,7 +12,7 @@ LEFT_HAND, RIGHT_HAND, NEAR_GRAB_PICK_RADIUS, DEFAULT_SEARCH_SPHERE_DISTANCE, DISPATCHER_PROPERTIES, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, PointerManager, getGrabPointSphereOffset, HMD, MyAvatar, Messages, findHandChildEntities, Picks, PickType, Pointers, - PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE, HIFI_EDIT_MANIPULATION_CHANNEL + PointerManager, print, Selection, DISPATCHER_HOVERING_LIST, DISPATCHER_HOVERING_STYLE */ controllerDispatcherPlugins = {}; @@ -524,16 +524,17 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); _this.setBlacklist(); } } - } else if (channel === HIFI_EDIT_MANIPULATION_CHANNEL) { - message = JSON.parse(data); - var tabletIDs = message.action === "startEdit" ? - [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] : []; - if (message.hand === Controller.Standard.LeftHand) { - _this.leftBlacklistTabletIDs = tabletIDs; - _this.setLeftBlacklist(); - } else if (message.hand === Controller.Standard.RightHand) { - _this.rightBlacklistTabletIDs = tabletIDs; - _this.setRightBlacklist(); + + if (action === "tablet") { + var tabletIDs = message.blacklist + ? [HMD.tabletID, HMD.tabletScreenID, HMD.homeButtonID, HMD.homeButtonHighlightID] : []; + if (message.hand === LEFT_HAND) { + _this.leftBlacklistTabletIDs = tabletIDs; + _this.setLeftBlacklist(); + } else { + _this.rightBlacklistTabletIDs = tabletIDs; + _this.setRightBlacklist(); + } } } } catch (e) { @@ -575,7 +576,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); var controllerDispatcher = new ControllerDispatcher(); Messages.subscribe('Hifi-Hand-RayPick-Blacklist'); - Messages.subscribe(HIFI_EDIT_MANIPULATION_CHANNEL); Messages.messageReceived.connect(controllerDispatcher.handleMessage); Script.scriptEnding.connect(controllerDispatcher.cleanup); diff --git a/scripts/system/controllers/controllerModules/inEditMode.js b/scripts/system/controllers/controllerModules/inEditMode.js index 3a1b835146..5709b19efe 100644 --- a/scripts/system/controllers/controllerModules/inEditMode.js +++ b/scripts/system/controllers/controllerModules/inEditMode.js @@ -20,6 +20,7 @@ Script.include("/~/system/libraries/utils.js"); var MARGIN = 25; function InEditMode(hand) { this.hand = hand; + this.isEditing = false; this.triggerClicked = false; this.selectedTarget = null; this.reticleMinX = MARGIN; @@ -212,6 +213,37 @@ Script.include("/~/system/libraries/utils.js"); enableDispatcherModule("LeftHandInEditMode", leftHandInEditMode); enableDispatcherModule("RightHandInEditMode", rightHandInEditMode); + var INEDIT_STATUS_CHANNEL = "Hifi-InEdit-Status"; + var HAND_RAYPICK_BLACKLIST_CHANNEL = "Hifi-Hand-RayPick-Blacklist"; + this.handleMessage = function (channel, data, sender) { + if (channel === INEDIT_STATUS_CHANNEL && sender === MyAvatar.sessionUUID) { + var message; + + try { + message = JSON.parse(data); + } catch (e) { + return; + } + + switch (message.method) { + case "editing": + if (message.hand === LEFT_HAND) { + leftHandInEditMode.isEditing = message.editing; + } else { + rightHandInEditMode.isEditing = message.editing; + } + Messages.sendLocalMessage(HAND_RAYPICK_BLACKLIST_CHANNEL, JSON.stringify({ + action: "tablet", + hand: message.hand, + blacklist: message.editing + })); + break; + } + } + }; + Messages.subscribe(INEDIT_STATUS_CHANNEL); + Messages.messageReceived.connect(this.handleMessage); + function cleanup() { disableDispatcherModule("LeftHandInEditMode"); disableDispatcherModule("RightHandInEditMode"); diff --git a/scripts/system/controllers/controllerModules/inVREditMode.js b/scripts/system/controllers/controllerModules/inVREditMode.js index 0c04918ab1..104e37d76c 100644 --- a/scripts/system/controllers/controllerModules/inVREditMode.js +++ b/scripts/system/controllers/controllerModules/inVREditMode.js @@ -18,7 +18,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); function InVREditMode(hand) { this.hand = hand; - this.disableModules = false; + this.isAppActive = false; + this.isEditing = false; this.running = false; var NO_HAND_LASER = -1; // Invalid hand parameter so that standard laser is not displayed. this.parameters = makeDispatcherModuleParameters( @@ -66,7 +67,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.isReady = function (controllerData) { - if (this.disableModules) { + if (this.isAppActive) { return makeRunningValues(true, [], []); } return makeRunningValues(false, [], []); @@ -74,14 +75,13 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); this.run = function (controllerData) { // Default behavior if disabling is not enabled. - if (!this.disableModules) { + if (!this.isAppActive) { return this.exitModule(); } // Tablet stylus. - var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND ? - "RightTabletStylusInput" : - "LeftTabletStylusInput"); + var tabletStylusInput = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightTabletStylusInput" : "LeftTabletStylusInput"); if (tabletStylusInput) { var tabletReady = tabletStylusInput.isReady(controllerData); if (tabletReady.active) { @@ -90,9 +90,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } // Tablet surface. - var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND ? - "RightWebSurfaceLaserInput" : - "LeftWebSurfaceLaserInput"); + var overlayLaser = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightWebSurfaceLaserInput" : "LeftWebSurfaceLaserInput"); if (overlayLaser) { var overlayLaserReady = overlayLaser.isReady(controllerData); var target = controllerData.rayPicks[this.hand].objectID; @@ -114,8 +113,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); // HUD overlay. if (!controllerData.triggerClicks[this.hand]) { var hudLaser = getEnabledModuleByName(this.hand === RIGHT_HAND - ? "RightHudOverlayPointer" - : "LeftHudOverlayPointer"); + ? "RightHudOverlayPointer" : "LeftHudOverlayPointer"); if (hudLaser) { var hudLaserReady = hudLaser.isReady(controllerData); if (hudLaserReady.active) { @@ -125,9 +123,8 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); } // Teleport. - var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND ? - "RightTeleporter" : - "LeftTeleporter"); + var teleporter = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightTeleporter" : "LeftTeleporter"); if (teleporter) { var teleporterReady = teleporter.isReady(controllerData); if (teleporterReady.active) { @@ -145,19 +142,39 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); enableDispatcherModule("LeftHandInVREditMode", leftHandInVREditMode); enableDispatcherModule("RightHandInVREditMode", rightHandInVREditMode); - var INVREDIT_DISABLER_MESSAGE_CHANNEL = "Hifi-InVREdit-Disabler"; - this.handleMessage = function (channel, message, sender) { - if (sender === MyAvatar.sessionUUID && channel === INVREDIT_DISABLER_MESSAGE_CHANNEL) { - if (message === "both") { - leftHandInVREditMode.disableModules = true; - rightHandInVREditMode.disableModules = true; - } else if (message === "none") { - leftHandInVREditMode.disableModules = false; - rightHandInVREditMode.disableModules = false; + var INVREDIT_STATUS_CHANNEL = "Hifi-InVREdit-Status"; + var HAND_RAYPICK_BLACKLIST_CHANNEL = "Hifi-Hand-RayPick-Blacklist"; + this.handleMessage = function (channel, data, sender) { + if (channel === INVREDIT_STATUS_CHANNEL && sender === MyAvatar.sessionUUID) { + var message; + + try { + message = JSON.parse(data); + } catch (e) { + return; + } + + switch (message.method) { + case "active": + leftHandInVREditMode.isAppActive = message.active; + rightHandInVREditMode.isAppActive = message.active; + break; + case "editing": + if (message.hand === LEFT_HAND) { + leftHandInVREditMode.isEditing = message.editing; + } else { + rightHandInVREditMode.isEditing = message.editing; + } + Messages.sendLocalMessage(HAND_RAYPICK_BLACKLIST_CHANNEL, JSON.stringify({ + action: "tablet", + hand: message.hand, + blacklist: message.editing + })); + break; } } }; - Messages.subscribe(INVREDIT_DISABLER_MESSAGE_CHANNEL); + Messages.subscribe(INVREDIT_STATUS_CHANNEL); Messages.messageReceived.connect(this.handleMessage); this.cleanup = function () { diff --git a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js index cbee8cabf7..e59b2e35ad 100644 --- a/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js +++ b/scripts/system/controllers/controllerModules/nearParentGrabOverlay.js @@ -9,7 +9,7 @@ /* global Script, MyAvatar, Controller, RIGHT_HAND, LEFT_HAND, getControllerJointIndex, enableDispatcherModule, disableDispatcherModule, Messages, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, makeDispatcherModuleParameters, Overlays, makeRunningValues, Vec3, resizeTablet, getTabletWidthFromSettings, - NEAR_GRAB_RADIUS, HMD, Uuid, HIFI_EDIT_MANIPULATION_CHANNEL + NEAR_GRAB_RADIUS, HMD, Uuid, getEnabledModuleByName */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -176,14 +176,23 @@ Script.include("/~/system/libraries/utils.js"); return null; }; - this.isEditing = false; - this.setIsEditing = function (editing) { - this.isEditing = editing; + this.isEditing = function () { + var inEditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHandInEditMode" : "LeftHandInEditMode"); + if (inEditModeModule && inEditModeModule.isEditing) { + return true; + } + var inVREditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHandInVREditMode" : "LeftHandInVREditMode"); + if (inVREditModeModule && inVREditModeModule.isEditing) { + return true; + } + return false; }; this.isReady = function (controllerData) { if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) - || this.isEditing) { + || this.isEditing()) { this.robbed = false; return makeRunningValues(false, [], []); } @@ -207,7 +216,7 @@ Script.include("/~/system/libraries/utils.js"); this.run = function (controllerData) { if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0) - || this.isEditing || !this.isGrabbedThingVisible()) { + || this.isEditing() || !this.isGrabbedThingVisible()) { this.endNearParentingGrabOverlay(); this.robbed = false; return makeRunningValues(false, [], []); @@ -235,28 +244,6 @@ Script.include("/~/system/libraries/utils.js"); enableDispatcherModule("LeftNearParentingGrabOverlay", leftNearParentingGrabOverlay); enableDispatcherModule("RightNearParentingGrabOverlay", rightNearParentingGrabOverlay); - function onMessageReceived(channel, data, senderID) { - var message; - - if (channel !== HIFI_EDIT_MANIPULATION_CHANNEL || senderID !== MyAvatar.sessionUUID) { - return; - } - - try { - message = JSON.parse(data); - } catch (e) { - return; - } - - if (message.hand === Controller.Standard.LeftHand) { - leftNearParentingGrabOverlay.setIsEditing(message.action === "startEdit"); - } else if (message.hand === Controller.Standard.RightHand) { - rightNearParentingGrabOverlay.setIsEditing(message.action === "startEdit"); - } - } - Messages.subscribe(HIFI_EDIT_MANIPULATION_CHANNEL); - Messages.messageReceived.connect(onMessageReceived); - function cleanup() { leftNearParentingGrabOverlay.cleanup(); rightNearParentingGrabOverlay.cleanup(); diff --git a/scripts/system/controllers/controllerModules/nearTabletHighlight.js b/scripts/system/controllers/controllerModules/nearTabletHighlight.js index 3ced0a9e87..2e046f5dc6 100644 --- a/scripts/system/controllers/controllerModules/nearTabletHighlight.js +++ b/scripts/system/controllers/controllerModules/nearTabletHighlight.js @@ -11,7 +11,7 @@ // /* global LEFT_HAND, RIGHT_HAND, makeDispatcherModuleParameters, makeRunningValues, enableDispatcherModule, - * disableDispatcherModule, HIFI_EDIT_MANIPULATION_CHANNEL */ + * disableDispatcherModule, getEnabledModuleByName */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -66,9 +66,18 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); 100 ); - this.isEditing = false; - this.setIsEditing = function (editing) { - this.isEditing = editing; + this.isEditing = function () { + var inEditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHandInEditMode" : "LeftHandInEditMode"); + if (inEditModeModule && inEditModeModule.isEditing) { + return true; + } + var inVREditModeModule = getEnabledModuleByName(this.hand === RIGHT_HAND + ? "RightHandInVREditMode" : "LeftHandInVREditMode"); + if (inVREditModeModule && inVREditModeModule.isEditing) { + return true; + } + return false; }; this.isNearTablet = function (controllerData) { @@ -76,7 +85,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.isReady = function (controllerData) { - if (!this.isEditing && this.isNearTablet(controllerData)) { + if (!this.isEditing() && this.isNearTablet(controllerData)) { return makeRunningValues(true, [], []); } setTabletNearGrabbable(this.hand, false); @@ -84,7 +93,7 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); }; this.run = function (controllerData) { - if (this.isEditing || !this.isNearTablet(controllerData)) { + if (this.isEditing() || !this.isNearTablet(controllerData)) { setTabletNearGrabbable(this.hand, false); return makeRunningValues(false, [], []); } @@ -116,28 +125,6 @@ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); HMD.mountedChanged.connect(onDisplayModeChanged); onDisplayModeChanged(); - function onMessageReceived(channel, data, senderID) { - var message; - - if (channel !== HIFI_EDIT_MANIPULATION_CHANNEL || senderID !== MyAvatar.sessionUUID) { - return; - } - - try { - message = JSON.parse(data); - } catch (e) { - return; - } - - if (message.hand === Controller.Standard.LeftHand) { - leftNearTabletHighlight.setIsEditing(message.action === "startEdit"); - } else if (message.hand === Controller.Standard.RightHand) { - rightNearTabletHighlight.setIsEditing(message.action === "startEdit"); - } - } - Messages.subscribe(HIFI_EDIT_MANIPULATION_CHANNEL); - Messages.messageReceived.connect(onMessageReceived); - function cleanUp() { disableDispatcherModule("LeftNearTabletHighlight"); disableDispatcherModule("RightNearTabletHighlight"); diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index f7b997a897..e9d5255d28 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -24,7 +24,6 @@ HAPTIC_PULSE_DURATION:true, DISPATCHER_HOVERING_LIST:true, DISPATCHER_HOVERING_STYLE:true, - HIFI_EDIT_MANIPULATION_CHANNEL:true, Entities, makeDispatcherModuleParameters:true, makeRunningValues:true, @@ -150,8 +149,6 @@ DISPATCHER_PROPERTIES = [ "userData" ]; -HIFI_EDIT_MANIPULATION_CHANNEL = "HiFi-Edit-Manipulation"; - // priority -- a lower priority means the module will be asked sooner than one with a higher priority in a given update step // activitySlots -- indicates which "slots" must not yet be in use for this module to start // requiredDataForReady -- which "situation" parts this module looks at to decide if it will start @@ -593,7 +590,6 @@ if (typeof module !== 'undefined') { TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE: TRIGGER_ON_VALUE, DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST, - HIFI_EDIT_MANIPULATION_CHANNEL: HIFI_EDIT_MANIPULATION_CHANNEL, worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix }; } diff --git a/scripts/system/libraries/entitySelectionTool.js b/scripts/system/libraries/entitySelectionTool.js index 4552ed27fb..e1427b0a51 100644 --- a/scripts/system/libraries/entitySelectionTool.js +++ b/scripts/system/libraries/entitySelectionTool.js @@ -14,7 +14,7 @@ // /* global SelectionManager, SelectionDisplay, grid, rayPlaneIntersection, rayPlaneIntersection2, pushCommandForSelections, - getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE, HIFI_EDIT_MANIPULATION_CHANNEL */ + getMainTabletIDs, getControllerWorldLocation, TRIGGER_ON_VALUE */ const SPACE_LOCAL = "local"; const SPACE_WORLD = "world"; @@ -639,6 +639,8 @@ SelectionDisplay = (function() { ROLL: 2 }; + const INEDIT_STATUS_CHANNEL = "Hifi-InEdit-Status"; + /** * The current space mode, this could have been a forced space mode since we do not support multi selection while in * local space mode. @@ -1118,11 +1120,12 @@ SelectionDisplay = (function() { activeTool = hitTool; that.clearDebugPickPlane(); if (activeTool.onBegin) { - Messages.sendLocalMessage(HIFI_EDIT_MANIPULATION_CHANNEL, JSON.stringify({ - action: "startEdit", - hand: that.triggeredHand - })); that.editingHand = that.triggeredHand; + Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ + method: "editing", + hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, + editing: true + })); activeTool.onBegin(event, pickRay, results); } else { print("ERROR: entitySelectionTool.mousePressEvent - ActiveTool(" + activeTool.mode + ") missing onBegin"); @@ -1271,9 +1274,10 @@ SelectionDisplay = (function() { if (wantDebug) { print(" Triggering ActiveTool(" + activeTool.mode + ")'s onEnd"); } - Messages.sendLocalMessage(HIFI_EDIT_MANIPULATION_CHANNEL, JSON.stringify({ - action: "finishEdit", - hand: that.editingHand + Messages.sendLocalMessage(INEDIT_STATUS_CHANNEL, JSON.stringify({ + method: "editing", + hand: that.editingHand === Controller.Standard.LeftHand ? LEFT_HAND : RIGHT_HAND, + editing: false })); that.editingHand = NO_HAND; activeTool.onEnd(event); From bf285b357065dfa83f9d4e20417f6f2a8355cdcc Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 21 Nov 2018 14:14:07 -0800 Subject: [PATCH 102/307] adding experimental login state sound --- interface/src/Application.cpp | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cb34ea8eee..9db78e42b2 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1317,6 +1317,16 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo connect(this, &Application::activeDisplayPluginChanged, this, [&](){ auto dialogsManager = DependencyManager::get(); auto keyboard = DependencyManager::get(); + if (_loginStateSoundInjector != nullptr) { + AudioInjectorOptions options; + options.localOnly = true; + options.position = getMyAvatar()->getHeadPosition(); + options.loop = true; + options.volume = 0.4f; + options.stereo = true; + _loginStateSoundInjector->setOptions(options); + _loginStateSoundInjector->restart(); + } if (getLoginDialogPoppedUp()) { if (_firstRun.get()) { // display mode changed. Don't allow auto-switch to work after this session. @@ -2967,15 +2977,15 @@ void Application::showLoginScreen() { loginData["action"] = "login dialog shown"; UserActivityLogger::getInstance().logAction("encourageLoginDialog", loginData); _window->setWindowTitle("High Fidelity Interface"); - if (!_loginStateSoundInjector) { - AudioInjectorOptions options; - options.localOnly = true; - options.position = getMyAvatar()->getHeadPosition(); - options.loop = true; - options.volume = 0.4f; - options.stereo = true; - _loginStateSoundInjector = AudioInjector::playSound(_loginStateSound, options); - } + //if (!_loginStateSoundInjector) { + // AudioInjectorOptions options; + // options.localOnly = true; + // options.position = getMyAvatar()->getHeadPosition(); + // options.loop = true; + // options.volume = 0.4f; + // options.stereo = true; + // _loginStateSoundInjector = AudioInjector::playSound(_loginStateSound, options); + //} } else { resumeAfterLoginDialogActionTaken(); } From 746c5ed621a0206fea789fc6d58242995ddf6b1c Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 21 Nov 2018 15:46:54 -0800 Subject: [PATCH 103/307] we can now handle missing joints and extra joints --- libraries/animation/src/AnimSkeleton.cpp | 3 ++- libraries/fbx/src/FBXReader.cpp | 22 +++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index e928658366..c5fb20d99b 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,6 +27,7 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { _fbxToHifiJointNameMapping = hfmModel.fbxToHifiJointNameMapping; buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); + // we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose // when we are dealing with a joint offset in the model for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { @@ -352,7 +353,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } int mirrorJointIndex = -1; mirrorJointIndex = containsLeft(_joints[i].name); - if (mirrorJointIndex > -1) { + if (!(mirrorJointIndex > -1)) { mirrorJointIndex = containsRight(_joints[i].name); } if (mirrorJointIndex >= 0) { diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 3ddcd75255..abf84d96b1 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -480,14 +480,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& std::map lights; QVariantHash joints = mapping.value("joint").toHash(); - QString jointEyeLeftName = processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); - QString jointEyeRightName = processID(getString(joints.value("jointEyeRight", "jointEyeRight"))); - QString jointNeckName = processID(getString(joints.value("jointNeck", "jointNeck"))); - QString jointRootName = processID(getString(joints.value("jointRoot", "jointRoot"))); + QString jointEyeLeftName = "EyeLeft"; //processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); + QString jointEyeRightName = "EyeRight"; // processID(getString(joints.value("jointEyeRight", "jointEyeRight"))); + QString jointNeckName = "Neck"; //processID(getString(joints.value("jointNeck", "jointNeck"))); + QString jointRootName = "Hips"; //processID(getString(joints.value("jointRoot", "jointRoot"))); QString jointLeanName = processID(getString(joints.value("jointLean", "jointLean"))); QString jointHeadName = "Head";// processID(getString(joints.value("jointHead", "jointHead"))); - QString jointLeftHandName = processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); - QString jointRightHandName = processID(getString(joints.value("jointRightHand", "jointRightHand"))); + QString jointLeftHandName = "LeftHand"; //processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); + QString jointRightHandName = "RightHand"; // processID(getString(joints.value("jointRightHand", "jointRightHand"))); QString jointEyeLeftID; QString jointEyeRightID; QString jointNeckID; @@ -1845,7 +1845,15 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& jointIndex = hfmModel.getJointIndex(hfmModel.fbxToHifiJointNameMapping[jointName]); } if (jointIndex != -1) { - hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); + //if (jointIndex == 18) { + // glm::quat spine3LocalOffset(0.94232035f, 0.000000014995882f, 0.33471236f, 0.000000058068572f); + // glm::quat spine3AbsoluteOffset = spine3LocalOffset * hfmModel.jointRotationOffsets[16]; + // hfmModel.jointRotationOffsets.insert(jointIndex, spine3AbsoluteOffset); + // qCDebug(modelformat) << "Joint Rotation Offset added for spine3 : " << spine3AbsoluteOffset; + //} else { + hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); + //} + } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } From 26fc53ef7808c86db8b7aab6fa7970c241dad536 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 26 Nov 2018 11:08:59 -0800 Subject: [PATCH 104/307] cleaned up whitespace and removed debug prints --- libraries/animation/src/AnimSkeleton.cpp | 40 +++++++++---------- libraries/animation/src/Rig.cpp | 1 - .../src/avatars-renderer/Avatar.cpp | 12 +----- .../src/avatars-renderer/SkeletonModel.cpp | 1 - libraries/fbx/src/FBXReader.cpp | 28 ++++--------- 5 files changed, 27 insertions(+), 55 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index c5fb20d99b..110dd5f92a 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -65,19 +65,14 @@ AnimSkeleton::AnimSkeleton(const std::vector& joints, const QMap AnimSkeleton::getChildrenOfJoint(int jointIndex) const { } const QString AnimSkeleton::getJointName(int jointIndex) const { - + QString jointName = _joints[jointIndex].name; QMapIterator i(_fbxToHifiJointNameMapping); while (i.hasNext()) { @@ -146,8 +141,7 @@ const QString AnimSkeleton::getJointName(int jointIndex) const { break; } } - //qCDebug(animation) << "reverse lookup: returning " << jointName << " for " << jointIndex; - return jointName; //;_joints[jointIndex].name; + return jointName; } AnimPose AnimSkeleton::getAbsolutePose(int jointIndex, const AnimPoseVec& relativePoses) const { @@ -222,7 +216,6 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { bool AnimSkeleton::checkNonMirrored(QString jointName) const { - //bool isNonMirrored = false; QMapIterator i(_fbxToHifiJointNameMapping); while (i.hasNext()) { i.next(); @@ -264,14 +257,15 @@ int AnimSkeleton::containsLeft(QString jointName) const { if (i.key().startsWith("Left")) { QString mirrorJointName = QString(i.key()).replace(0, 4, "Right"); mirrorJointIndex = nameToJointIndex(mirrorJointName); - //return true } } } - if (jointName.startsWith("Left")) { - QString mirrorJointName = QString(jointName).replace(0, 4, "Right"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); - } + if (mirrorJointIndex < 0) { + if (jointName.startsWith("Left")) { + QString mirrorJointName = QString(jointName).replace(0, 4, "Right"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } + } return mirrorJointIndex; } @@ -285,12 +279,14 @@ int AnimSkeleton::containsRight(QString jointName) const { if (i.key().startsWith("Right")) { QString mirrorJointName = QString(i.key()).replace(0, 5, "Left"); mirrorJointIndex = nameToJointIndex(mirrorJointName); - } + } } } - if (jointName.startsWith("Right")) { - QString mirrorJointName = QString(jointName).replace(0, 5, "Left"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); + if (mirrorJointIndex < 0) { + if (jointName.startsWith("Right")) { + QString mirrorJointName = QString(jointName).replace(0, 5, "Left"); + mirrorJointIndex = nameToJointIndex(mirrorJointName); + } } return mirrorJointIndex; } @@ -353,7 +349,7 @@ void AnimSkeleton::buildSkeletonFromJoints(const std::vector& joints, } int mirrorJointIndex = -1; mirrorJointIndex = containsLeft(_joints[i].name); - if (!(mirrorJointIndex > -1)) { + if (mirrorJointIndex < 0) { mirrorJointIndex = containsRight(_joints[i].name); } if (mirrorJointIndex >= 0) { diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index 1675ea8e90..5e2610e37e 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -423,7 +423,6 @@ int Rig::indexOfJoint(const QString& jointName) const { int result = _animSkeleton->nameToJointIndex(jointName); if (_animSkeleton->getFBXToHifiJointNameMapping().contains(jointName)) { - //qCDebug(animation) << "the alternate name for the joint " << jointName << " is " << _animSkeleton->getFBXToHifiJointNameMapping()[jointName] << " " << _animSkeleton->nameToJointIndex(_animSkeleton->getFBXToHifiJointNameMapping()[jointName]); result = _animSkeleton->nameToJointIndex(jointName); } diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 14e97a5bf9..f3017049a7 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1437,21 +1437,11 @@ int Avatar::getJointIndex(const QString& name) const { withValidJointIndicesCache([&]() { if (_modelJointIndicesCache.contains(name)) { result = _modelJointIndicesCache[name] - 1; - if (_skeletonModel && _skeletonModel->isActive()) { - // qCDebug(avatars_renderer) << "the other head id is this" << _skeletonModel->getHFMModel().fbxToHifiJointNameMapping["Head"]; - // qCDebug(avatars_renderer) << "joint indices cache " << name << " " << _modelJointIndicesCache[name]; - if (_modelJointIndicesCache.contains(_skeletonModel->getHFMModel().fbxToHifiJointNameMapping["Head"])) { - // qCDebug(avatars_renderer) << "joint alternate name " << _skeletonModel->getHFMModel().fbxToHifiJointNameMapping["Head"] << " " << _modelJointIndicesCache[_skeletonModel->getHFMModel().fbxToHifiJointNameMapping["Head"]]; - } - } } else { - // doesn't contain name. - //qCDebug(avatars_renderer) << "name is not here"; + // doesn't contain name. check the fbx-to-hifi joint name mapping if (_skeletonModel && _skeletonModel->isActive()) { - if (_modelJointIndicesCache.contains(_skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name])) { result = _modelJointIndicesCache[_skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name]] - 1; - // qCDebug(avatars_renderer) << "joint " << name << " remapped to " << _skeletonModel->getHFMModel().fbxToHifiJointNameMapping[name] << result; } } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index a97ba4bf4b..36e37dd3d4 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -317,7 +317,6 @@ bool SkeletonModel::getEyePositions(glm::vec3& firstEyePosition, glm::vec3& seco } glm::vec3 SkeletonModel::getDefaultEyeModelPosition() const { - //qCDebug(avatars_renderer) << "default eye model position " << _defaultEyeModelPosition; return _owningAvatar->getModelScale() * _defaultEyeModelPosition; } diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index abf84d96b1..35aabca123 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -423,11 +423,9 @@ QMap getJointNameMapping(const QVariantHash& mapping) { if (!mapping.isEmpty() && mapping.contains(JOINT_NAME_MAPPING_FIELD) && mapping[JOINT_NAME_MAPPING_FIELD].type() == QVariant::Hash) { auto jointNames = mapping[JOINT_NAME_MAPPING_FIELD].toHash(); for (auto itr = jointNames.begin(); itr != jointNames.end(); itr++) { - qCDebug(modelformat) << "found a joint mapping field key " << itr.key() << " value " << itr.value().toString(); fbxToHifiJointNameMap.insert(itr.key(), itr.value().toString()); qCDebug(modelformat) << "the mapped key " << itr.key() << " has a value of " << fbxToHifiJointNameMap[itr.key()]; } - } return fbxToHifiJointNameMap; } @@ -480,14 +478,14 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& std::map lights; QVariantHash joints = mapping.value("joint").toHash(); - QString jointEyeLeftName = "EyeLeft"; //processID(getString(joints.value("jointEyeLeft", "jointEyeLeft"))); - QString jointEyeRightName = "EyeRight"; // processID(getString(joints.value("jointEyeRight", "jointEyeRight"))); - QString jointNeckName = "Neck"; //processID(getString(joints.value("jointNeck", "jointNeck"))); - QString jointRootName = "Hips"; //processID(getString(joints.value("jointRoot", "jointRoot"))); + QString jointEyeLeftName = "EyeLeft"; + QString jointEyeRightName = "EyeRight"; + QString jointNeckName = "Neck"; + QString jointRootName = "Hips"; QString jointLeanName = processID(getString(joints.value("jointLean", "jointLean"))); - QString jointHeadName = "Head";// processID(getString(joints.value("jointHead", "jointHead"))); - QString jointLeftHandName = "LeftHand"; //processID(getString(joints.value("jointLeftHand", "jointLeftHand"))); - QString jointRightHandName = "RightHand"; // processID(getString(joints.value("jointRightHand", "jointRightHand"))); + QString jointHeadName = "Head"; + QString jointLeftHandName = "LeftHand"; + QString jointRightHandName = "RightHand"; QString jointEyeLeftID; QString jointEyeRightID; QString jointNeckID; @@ -1845,21 +1843,11 @@ HFMModel* FBXReader::extractHFMModel(const QVariantHash& mapping, const QString& jointIndex = hfmModel.getJointIndex(hfmModel.fbxToHifiJointNameMapping[jointName]); } if (jointIndex != -1) { - //if (jointIndex == 18) { - // glm::quat spine3LocalOffset(0.94232035f, 0.000000014995882f, 0.33471236f, 0.000000058068572f); - // glm::quat spine3AbsoluteOffset = spine3LocalOffset * hfmModel.jointRotationOffsets[16]; - // hfmModel.jointRotationOffsets.insert(jointIndex, spine3AbsoluteOffset); - // qCDebug(modelformat) << "Joint Rotation Offset added for spine3 : " << spine3AbsoluteOffset; - //} else { - hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); - //} - + hfmModel.jointRotationOffsets.insert(jointIndex, rotationOffset); } qCDebug(modelformat) << "Joint Rotation Offset added to Rig._jointRotationOffsets : " << " jointName: " << jointName << " jointIndex: " << jointIndex << " rotation offset: " << rotationOffset; } - - return hfmModelPtr; } From fb7b503ce94ce8da1aaea43dc21d7b5905662ae3 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 26 Nov 2018 13:48:00 -0800 Subject: [PATCH 105/307] removed unnecessary iteration of the joint name mapping in AnimSkeleton.cpp --- libraries/animation/src/AnimSkeleton.cpp | 54 ++++-------------------- 1 file changed, 8 insertions(+), 46 deletions(-) diff --git a/libraries/animation/src/AnimSkeleton.cpp b/libraries/animation/src/AnimSkeleton.cpp index 110dd5f92a..c5e6241555 100644 --- a/libraries/animation/src/AnimSkeleton.cpp +++ b/libraries/animation/src/AnimSkeleton.cpp @@ -27,7 +27,6 @@ AnimSkeleton::AnimSkeleton(const HFMModel& hfmModel) { _fbxToHifiJointNameMapping = hfmModel.fbxToHifiJointNameMapping; buildSkeletonFromJoints(joints, hfmModel.jointRotationOffsets); - // we make a copy of the inverseBindMatrices in order to prevent mutating the model bind pose // when we are dealing with a joint offset in the model for (int i = 0; i < (int)hfmModel.meshes.size(); i++) { @@ -133,13 +132,8 @@ std::vector AnimSkeleton::getChildrenOfJoint(int jointIndex) const { const QString AnimSkeleton::getJointName(int jointIndex) const { QString jointName = _joints[jointIndex].name; - QMapIterator i(_fbxToHifiJointNameMapping); - while (i.hasNext()) { - i.next(); - if (i.value() == _joints[jointIndex].name) { - jointName = i.key(); - break; - } + if (_fbxToHifiJointNameMapping.contains(_fbxToHifiJointNameMapping.key(_joints[jointIndex].name))) { + jointName = _fbxToHifiJointNameMapping.key(_joints[jointIndex].name); } return jointName; } @@ -216,22 +210,8 @@ void AnimSkeleton::mirrorAbsolutePoses(AnimPoseVec& poses) const { bool AnimSkeleton::checkNonMirrored(QString jointName) const { - QMapIterator i(_fbxToHifiJointNameMapping); - while (i.hasNext()) { - i.next(); - if (i.value() == jointName) { - // check for left right in the key - if (i.key() != "Hips" && i.key() != "Spine" && - i.key() != "Spine1" && i.key() != "Spine2" && - i.key() != "Neck" && i.key() != "Head" && - !((i.key().startsWith("Left") || i.key().startsWith("Right")) && - i.key() != "LeftEye" && i.key() != "RightEye")) { - //return true - return true; - } else { - return false; - } - } + if (_fbxToHifiJointNameMapping.contains(_fbxToHifiJointNameMapping.key(jointName))) { + jointName = _fbxToHifiJointNameMapping.key(jointName); } // check the unmapped name if (jointName != "Hips" && jointName != "Spine" && @@ -239,26 +219,16 @@ bool AnimSkeleton::checkNonMirrored(QString jointName) const { jointName != "Neck" && jointName != "Head" && !((jointName.startsWith("Left") || jointName.startsWith("Right")) && jointName != "LeftEye" && jointName != "RightEye")) { - //return true return true; } else { return false; } - } int AnimSkeleton::containsLeft(QString jointName) const { - QMapIterator i(_fbxToHifiJointNameMapping); int mirrorJointIndex = -1; - while (i.hasNext()) { - i.next(); - if (i.value() == jointName) { - // check for left right in the key - if (i.key().startsWith("Left")) { - QString mirrorJointName = QString(i.key()).replace(0, 4, "Right"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); - } - } + if (_fbxToHifiJointNameMapping.contains(_fbxToHifiJointNameMapping.key(jointName))) { + jointName = _fbxToHifiJointNameMapping.key(jointName); } if (mirrorJointIndex < 0) { if (jointName.startsWith("Left")) { @@ -270,17 +240,9 @@ int AnimSkeleton::containsLeft(QString jointName) const { } int AnimSkeleton::containsRight(QString jointName) const { - QMapIterator i(_fbxToHifiJointNameMapping); int mirrorJointIndex = -1; - while (i.hasNext()) { - i.next(); - if (i.value() == jointName) { - // check for left right in the key - if (i.key().startsWith("Right")) { - QString mirrorJointName = QString(i.key()).replace(0, 5, "Left"); - mirrorJointIndex = nameToJointIndex(mirrorJointName); - } - } + if (_fbxToHifiJointNameMapping.contains(_fbxToHifiJointNameMapping.key(jointName))) { + jointName = _fbxToHifiJointNameMapping.key(jointName); } if (mirrorJointIndex < 0) { if (jointName.startsWith("Right")) { From c53febceacf06e1d380a915aac0c871b289947f8 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 26 Nov 2018 15:10:04 -0800 Subject: [PATCH 106/307] adding oculus to login dialog/adding focus wip --- .../hifi-glyphs-1.33/fonts/hifi-glyphs.woff | Bin 21816 -> 0 bytes .../fonts/hifi-glyphs.eot | Bin 33958 -> 34130 bytes .../fonts/hifi-glyphs.svg | 1 + .../fonts/hifi-glyphs.ttf | Bin 33780 -> 33952 bytes .../hifi-glyphs-1.34/fonts/hifi-glyphs.woff | Bin 0 -> 21976 bytes .../icons-reference.html | 8 + .../styles.css | 3 + interface/resources/fonts/hifi-glyphs.ttf | Bin 33780 -> 33952 bytes .../qml/LoginDialog/CompleteProfileBody.qml | 4 +- .../qml/LoginDialog/LinkAccountBody.qml | 586 ++++++++++++------ .../qml/LoginDialog/LoggingInBody.qml | 69 +-- .../resources/qml/LoginDialog/SignInBody.qml | 514 --------------- .../resources/qml/LoginDialog/SignUpBody.qml | 571 +++++++++++------ .../qml/LoginDialog/UsernameCollisionBody.qml | 4 +- .../resources/qml/stylesUit/HifiConstants.qml | 1 + interface/src/ui/LoginDialog.cpp | 5 + interface/src/ui/LoginDialog.h | 1 + 17 files changed, 826 insertions(+), 941 deletions(-) delete mode 100644 interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff rename interface/resources/fonts/{hifi-glyphs-1.33 => hifi-glyphs-1.34}/fonts/hifi-glyphs.eot (92%) rename interface/resources/fonts/{hifi-glyphs-1.33 => hifi-glyphs-1.34}/fonts/hifi-glyphs.svg (99%) rename interface/resources/fonts/{hifi-glyphs-1.33 => hifi-glyphs-1.34}/fonts/hifi-glyphs.ttf (92%) create mode 100644 interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff rename interface/resources/fonts/{hifi-glyphs-1.33 => hifi-glyphs-1.34}/icons-reference.html (99%) rename interface/resources/fonts/{hifi-glyphs-1.33 => hifi-glyphs-1.34}/styles.css (99%) delete mode 100644 interface/resources/qml/LoginDialog/SignInBody.qml diff --git a/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.woff deleted file mode 100644 index 842065af361c0063b156f7577f012e881e897c8d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 21816 zcmV(;K-<4}Pew*hR8&s@097~u3jhEB0C?;G0RR91000000000000000000000000( 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

    <4 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 NXZ2>Syy*X%9|5QGYkvR$ diff --git a/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg similarity index 99% rename from interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg rename to interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg index 554a3999be..de91dcae71 100644 --- a/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.svg +++ b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.svg @@ -155,4 +155,5 @@ + diff --git a/interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.ttf b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.ttf similarity index 92% rename from interface/resources/fonts/hifi-glyphs-1.33/fonts/hifi-glyphs.ttf rename to interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.ttf index b61455a9f11a4703fbd55e8a319b2b2b929ab632..c27553333bda29cdfd06e4c1ebdd964b83ff6175 100644 GIT binary patch delta 598 zcmYLFPiRtc9RGZO@A;nTTCj^AOP(&fbqG7^U=BYH|hZ00kD`VsHNA=w>rQM0M=Mh z%cY|ua6o$*fO#eVI4jO?O#ngXPfS+v*#Q#oS{yr(BnZoMY}-(P9y$ zjwD(vcKZpW`woF)j1wHe^`OUYW{fx6gX>w5h)A?WZ2y7Ys!Jp)2t$FQZ8t)vl(3?N xqY-6V8*ciwUyW7j=}XOX#HgchuAMbmgkq|iua*I5yBj|0YF{?wp_6Z_{ePAZeEk3b delta 421 zcmYL^JxCh?9L0bCyCh9%Ra$N+5+)H8ud zODDk-b?6}NRJv#m#G%l|(Om}z#}0KfOBIyFLLWTdg9nfI_C70DpOicT)KEa>O*A?_ zy4rH`3W{eyV0dsS$}v7D9s!MGZ(nwf{T#`GOac?LNi+Q;^!*x?-=O3c%uM?B78Q#7 zfPZ26{d`}4?^Ez?f%PX5H|L)C7fs+C=t?BwdDElI1&9VTCz4jSDONuKvJcX=oSHSQ z8|NK(=RowNnN3rc8IVY2(2ALiFU@Nw@Sy;5m`-J^m5;x=!H$40TRkUW6uy0p)peJ7 zq)@?WSu5SEqLntKM|PZEnU=8oMP7?hNz{J~R4Wh*wdw+Rs^9(ix$j!L2)obJA#r1# zT5*@Wt>W(bE-kldwMHI<2x6c+VY~fdC}`-xPThz&2ljJk!(Nmf_pjX{Zd?9^*O^*x PkZmWoUjO+2IIVOCHHT+~ diff --git a/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff b/interface/resources/fonts/hifi-glyphs-1.34/fonts/hifi-glyphs.woff new file mode 100644 index 0000000000000000000000000000000000000000..6d469701366c67d5aa8123d2be5b314f3f27b65a GIT binary patch literal 21976 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 literal 0 HcmV?d00001 diff --git a/interface/resources/fonts/hifi-glyphs-1.33/icons-reference.html b/interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html similarity index 99% rename from interface/resources/fonts/hifi-glyphs-1.33/icons-reference.html rename to interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html index 2c0ebd1885..1c75e9e235 100644 --- a/interface/resources/fonts/hifi-glyphs-1.33/icons-reference.html +++ b/interface/resources/fonts/hifi-glyphs-1.34/icons-reference.html @@ -607,6 +607,10 @@

    +
  • +
    + +
  • Character mapping

      @@ -1202,6 +1206,10 @@
      +
    • +
      + +