From 72851acd8b2117482d12f79297dc5ed87b829c16 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Mon, 1 Oct 2018 16:44:01 -0700 Subject: [PATCH 001/147] 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 002/147] 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 003/147] 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 004/147] 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 005/147] 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 006/147] 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 007/147] 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 008/147] 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 009/147] 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 010/147] 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 011/147] 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 012/147] 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 013/147] 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 014/147] 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 015/147] 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 016/147] 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 017/147] 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 z^JTIWiZf-h3^?EsxseF2yc0xdroFR1iHd2c>jZb{7$5- zY~Z>3*(8X-td+?IpMTB|LJZ2O1PFuCxi53?zP9|TudY~rRj`@1X1j*THNCU%F!Ay9 z6x~FA`r7O72KG7%mjl5Uq#52!X~eE%!OjO`pX5@RIEcu20G#18ScUYV8+XtH40-`H zLvl$g0dg4cn&2!GX8{Y|ZJMS?Je$m+;10Mws)T;?tqt@sCMgLiyqig71B>Ps&MgGX zezaO%O;`7?+qki5HgYiw!T{plIKG&;0 zuV20D+VSeJEIe172VaMW0kF}}RSuU!(EbDqo)Mo0pD5sb0`#?L2JBO+jI!Kug9e6- zzCCn^zP%uWk^q&NNrL<6h6`Z)k5mW1K2klBNzGSRj&bVf75n8rFgnioJ=e6o|8eCB z<>1Kvty?$G?i}8uYv4g@DI)umVjB3kyCd1ew-Fiacrevvf(}67hzOtHNx&;eRr=2g z#*Q@bG>rtgk)>Wu1tjJR8dk&568Ik~RFd*iPRJ9h#F$}iKnoMB%3$mc)~n(N1Bj|h zQ7vi(3w|m%jDq5rABv2Ihj=3mDAX-2qgnY>KABBy-8fP$KStN~uWM>1O-<_tR)aF~ zKL%XWw4K0kq> zP81fSeE|&AQ{0Gv?qgz9iiv3%NDBSR;AFQkG$kW5Q;f@rR3K_U$IG~Hk!nhW!R*A1 z1gs4G;FO^ZAi!LiQLB_M!YemyjS@u#6{^YstPmI!n5YP23FO*$xf^jFh*k_ixf3uH zwCyy7sQ#P+Z)z&wx5Mg!%Zy@DUs*{M$xM-2f*a1!$TEC}pHceEQE09_W*&pY=$0GG4 zh4d>es*Jr;Iq16RF8*c4?9FtSLT0Kc0O*Ju2HjDBu*d!?SEn+v7gnV1Kve06r5RQF z)jle!f=;t?R?f)88j@&G@l$tJ1ji2kfW>hG4%vxOQ8gg5Ss^E7BvS@OjR0d+oVyZK zSaZhOJUUG<$jZ1RrbLvP#%ObEw234_oG>8uDIuED1XIW*i~O*Fj0Vm$Wf6NW@Cf!uk&ysLLHm{i9O64r+ELm*v|Z0AS(+6$w@$S38`AxaLrqqHwlC9fh0Sng zfXvz-{H(hPcqJMftwZSg>96v&ue#m5D+A) z3f2y>ORNO~JKzk@FrtFtV9{f43_quhv?W88cE(YTU|iUabzxlCKFj_SXBoJqQes`R zK=PUqiS__{tM2xXN;O%vKxlzl!I*MnijgoJF&}o8{qAa$$OH@S3z}f6u4*!I&REKo zx&icoNyh%1@4Qxv`4L`T%tp^xW z$SAfo!F_q*W4#;-er-O;28u<{P*3r|-56YU6%UUwU_leg z+Ukkw<1Nvw{V!U=6=*3MqSrE(P3CjiOg@*$#$$;@Jn&e^1L42j5rVMD-fG{;J-vVb zQ%~3Ie`Hln%_FP!*E|)pFRR+OedBseRH)s!eeb^Q+xLP8;8c5PSIH~iVSrmq>>s>V zf!uWm_)F+6scuxBpkMU}IBARQ;;&A!tv`0c<kRZAEC^HT28g)4S*mn~d0m$kDT zkQwYDa^o>Z)CI*QwTv-c^npmdb9gt4HC&s?y$kDDAUEE`h;9ww1z<<=)=dm>gzR#S z;_@)4EwbA_T+d<+j29d3U0D4(dFvRXc{LWFm-v3`vB5NngXz->)oYVcd z?nP0U2N9-34y?QAA_jeBD&o$fDv=!<7*)bmi*gy(_6HvvSO?QE=D{kP-o$RQk8!9Y zC6#R0!AQ8}i=D`lL@v5$-2rA^)(9tLjdMEdp~jmgu}}9Omr?q>j3Vm}eDHxAe5V>r z#IWl)R7fz)#_jGft_SN5vjm1YnPP%~)bfT=aMR7jzBi1n0O<3>Aa|@g@Z0h*DzQ6l z>M<_9m3efbiT)vq9$2@ej4qg~i>~b(U36)fQstuSw}7sbsi^CUOOd%&yOG<_-L!0&%cHnlLG@KshM^j^o zLaHbZ(;2!YyM2WG?eu3jniI3=cS3U-rwI|GFhT-~DHZzG(>mBR#Q9$=Qb)x%ZZwbj zU62807ti+pVoh8Prfz_FYLU6G_Hh(E>NGURT}v>RB|)n1X$C_gPLlv|cfL2%XOfWF ztM$-98kWMsfH=qxrX%T?kmM6W3f|y6p?~ExuwYuHEm3nrq^=>Oj^-zG)0vsfq%ltO z6nqbfZn-A#8Dwob!qYjHOvyXe*kT<<70(+0A8F`X195);Ys=t#H}F7+3BGdtGItY3X1$(u`nw`*jJ@m&JP~TKkp;4gRMEoN9lR#`RQ=IFc)k!)@zQxkfbSYYl6vJj8t|yw7A|rj79%NZ5A%u$qRtW7-uwiBLZI(`? zMg~iRr9P_{MDE|t=MuIr>wnAfadF$1^B;qPdu7qw>GtljfRJ6`K+gYzAAjR7_jjM) z6-(&$K=7mHhFKEdIs|?!wvQ zRB|-RgUOXh#DplIJ^`qY>4|J2%jao<*a?;vja(L9c>__D@E2&FhuaAwP9wmQmxJ#9 z_}m@bhb}wN?0?klvBx;<=<>N+xhodhPdcCF;>lDZ;6(h1q?HQ}vlx~S*bzU5=7SmM zZZ3xfaA^i8WuR_6;Oz0okJrrw?848v0m^!}9{KPs+yj8tINmMXR~FjG&M$vnS^k`d z#JQ*{p7I)El*t`G84nJyE_6qnFLOETBrsAq)?WYdCNS2sRVV+lS9ufg080dp&-mDR zUQf`o+sQ}g?X2apAD(dHqa)cbcoTNpuljKNRera36yvAQYYI=X{_TAKhqO^rJHq|WD?mrVhJHr7ej#>vf%tg)MmjS3?EZbPzy4R_hxd`k_m4SeIDxUfyP5lUElWT87_(hcY`m`Cu@rj5qq^?^+X3o#3kbVcqzAc>(;${o43~1H8t1P zZfo8HF8ONvOs>0^0k4XW&{CQeMjPnM5vW zU``y!k9dMyeGSvu(IfPafGMD1&uI~Gl$O#^OsTxVkMxfvCU6sErZ7ECe)1D$Y%Dhg zaLvT0LSrt+WVv6yvFx%Sz4RLAi?b(9$? z4h{sJb!>km(jRbs;qQ+WM}l^a9WD-y1QvmR_cnKrbEbW>?fas?_Xr2NPyp|#(Ekg{ z6!Wo2(DAX6SS%87ZuUoF`C`yMlP%`+MQHjndzQ;bp=+vZ5^S3tbbDdz)D)ST+6rdjcDgk>)kQ|X!qM5- zOz(JTyglDaTWNbN)Z5z|YX?qi&9{$-#(HOBnamx*4GUoBc1`RSQR| zzPj+~`?$6-RxL1KXeeL<4C!JdD6p8VmK2jxT!@N9YaXX`gvn|dHKXNq)1o89U^hi` zt${w!;Jqm_0je-z2-%#WXS2K>Pw)b6Y>dw_oO6m(W2Y$NoMLx#CK#4FxW*rMY8W&w zg|wK4Kae#kKm620Q-CVE2{mk}Vd8QkVdp{YJeT!4houOeZ~h%O(bmJ7D@v5X9h6fz zkRywprqrpjucg$kdK!lOjD0qzyEZ?u;VuK7bwY$`rFfut%f)`sv9W-2EqrJ5!C$wq zNt!gojF?fgCWzZJ>_y;&8LAr`$=l~}Tj-YjB)kfn=~h7hmhOo)hnCSbYkGR3EwmXZ z)jH8lCcC%9Fl13*HBF}zm{3naj7!qV zL!>gaU}aQXVhA2MgJ5a#-~&im%zpr5R$)>0gCPe4=!sMb<1;X=WSJ(IC^Wu`Ksr&$saXFJ>&D3{nfwUT$bhI&2fw5|J0mn(*Llz zicx$peXBY2kY$VogM&y!(MbTGG5`%y1@l{UVsEmutaIOPZfGZ?pbf2%_x)~io=k=s z8T)ey?nB1)pQ1@A#V2__Ev88Cs>>+zA%osC;1!QODVcSq z{otSsW@Fh{o-JcvUGozl-Lcm^+`dE1k-^zg2=k@4(05I`Et- z_smt+u{IRy?8E-LW89wlI{)8Vce~$vz$vNQy~jUy{$gl_p(w2F2DFad}BtboR9vA zh&U?a)y*6-h?hQur(+aUQL4+CuBLM1u832aWbzm^Q3X6QQx1i>#;Pa^64)40T8207 zjwLadTTQDN>EPYWPZ48F4a-ZvdxVu2BZki?TTb03foiC!GMFvYYN$(k^^d*L3BnSRw zO)4k9iYm?rkb_8)-GGqf`gD@}8=#fvdvyO@22*ht!_DtTDH*b#<*r`1$X;dFaV7h6 z11qUtHZWo)YRAKRm^)F%m3$mmDaFlv$H=)G&XO=+*xklcaX&-@qQKJMRz=;sE$@sH zt`ZM6(G9k_PNybm4kN&&k zkH(D)D%nxsHu$MaY@j?-CZ&6d0W^_>o7^hKDBjt_MPwGxs(DbQF^^3~jABp_5&IFe zs%hmoT7eB=Fbg~w%zah?F+>^k<3^MW(@7wt66zKaFCj-FpTj^T5yO}zChY?gTSyqi zP|*)US4f2)3dXri@+&sHn)9$UFUdYkC091WvLjMiqLss~-qO?D^I+af@u=jo8ZJ1+ zrIO+%-~g|!v!Ca}&Kbksq<#hF1`Kl|bgSX~RKtbL#5eA|S+XU&dzmgpb(|RXmiB_U z80g_a*)>&t2pG*10sU2H zhj$OxkI>GiCJ*-0$j0#YezdyQx@*Yh4wdD&7ZS!D;qu#!%_2v+5NE&!D6@)MUh|4O zDs}NKxvN9(+8?Z> zl1yIQbk!9Aqt^^7VKci*XeoV5@Xl7caS;X#T3)(wwvAOO4=lj?14dcp7-`(HT$ zO(>9z6wg4Z?s2_Azv3o!p}1T(MCo>>+(s?;p_Ic{Uf-_v#q;Q^w_OlyWFkEQ>rmkC4^`;%)i3dCwqKP_U3R*mly1nRq7@ z01UN!z)vN^1#Uq1&*z|=Sbg4!K-KM^Y-{BVH-sDRhEleuUClc37UxPMVBg^9{VsPw z8I1Fv+kIRl7b_NX`C=iSk3{0JXyE?k+%*gD?Bl++@UfSn^b;q*8G(c+|akR1WuXMFNK5W6;=M@B|n21}GH00V07nmO$V3mCtgKe7sQ1 z<%-344(`Mw0cXKp?_wS<#&VHp3@={yJ{Qf$iiJG%5z9y2_=g?sg6xzOCax}EGu-&((}#W{9j5yuJ3EHr z*@Tr!CxN{2#Po zb3sl8E&|Fqw>qC(eTjYMGRC8+%UD3fE)g9f0iECL-ygVZB+MARlHz$+<$B(OM*2&u z`xs9Zfj-v2sxUPRkoLsr@GC%Q-bkCkXT|VH`{s@=&529b75))CbNdjxtvN#6wh#&hA-i3=+G-K83TH zcWxPA{y!2ZfaI;*!56MTt0r&n=GNZ#3oK^m&ftz6U3t?@EAjv6v13P%9t*zge1*$r zbGdvX8w;+X%9{+9;uuPjE=GJLtYpTOFe)YA0r&1t_MghZHr}b=F8tl^Sn0RiMZf!i z#dDq+P97GA`I3miT+KAHqLwluT0|MtI)%<;mzYS1DU%3b9jGRrVpA<8qhyr~Y4{08 zSJJg~B}1cpA|`r=jgu7Qj2SCNL2Q*ZtSkP&P>3n7q$ z{%uZfuY&y_}m0 zjdgW~LS3C>p-od`W19l;o4M)9Xh*Q09SGG8ZlrCrGuu8zM%n4j1z2nLZ|5%H#(O4u z#(RLW@TUVXqTW9;Fx)?ki5`N0cxNJkep*^M<}S%9X&ZPCc!{r6vXc~1{IHiF#4 z_XxcLaV1uj6 z@JBik6U6f4I#L>MMS(wJ1hV5b%dc=?e5Jhk&v7)-?(qz=-kEKv=C27Jj5-7Z^eFz zyLn;uF87Dm_8Uh&dc`J-p@SR#{`C>(>(HZfj~zX6`?L0EIQtr(VD{ z&bOD{1XgUXh-ELDlAYMzb+5s}RY2T$lKG zbImmZF{J;c_c;(QXlGy$%k{-EofSP_*NXPUkw~CEcqcLj&M=@N3$9oNO5L}c!-$mk zj2hSZ_ELddb6ba|ZpuA4*I>Q656sZ;*)?7&ttflBL}gnBDr?|#Fd!Kv^BMFkEvJjSsF7q{k%)x;o*blrFBPt8qNFP2Pjo~v2OTk~$<)gZ z#~gPJqy6AV07XRE!=?r$rEG9`o{=Q!x@&8A-m1WBx@V5edrkJ=3|$L)OEP7ogbX z7!qa6uBD4*IzKWVc&?*R}DH;kqj=+05j<#c!`C0DLpKjaz@T7 zS!yU*wWJq}p&WW#GEf}Q=I$A|{0;vYuJR55Amo5i&jZg5nMHkA&4Mdr5n7_AYZbc3 zM>GI4D`mtCr4rE8y-vMQ3`u7h&*1}t8I*b<94`#)q8+a2qNETv=Bq?AVI!>6>-C9^ zVc=^p6a-7nSz68j#@5LGp{I=f1G~brExEeMYaUv9P1m~EYHP!6^HkqHZLhw6X3xF? znXsm$vBX$>sNd=}d!(*tUvwZ8?NNJ`P(hgB$IPiQOffsl?%)pYYFu00(6BbxPy5YC zPNwBUFR(_3Mp1{mXJhcd4L&3)Ps!}$!Y&s`+FwD+$Pqwvncee)9XTQ&7 z?ZxI%7-SQjl*gZuo>I3cMLA>Yd6($4n%<@K=18O%5n@UhP!y&SnxKPmS-2zBAYq0= za()*;Pnp0oBlJnRMDy}?z&p7O=9Bqa#_+XS{c!XM%bn!8LhtU1LxpMKQ?j z5a%3@nixCNP!|AG%c4lW{&lqLL|vE!vvQ)_jkROkMFp`=cT&To_6i^eW5Pvhk}Ygj zQW62@cK@O|zkN1m=8b|@$cvZ(A`I|@$-!`IPgi??I5CjwPd_^Nu(@W6tlhM6-{6yj zPa4l`Iy8A;_trxtBL9A;qI{VdQbv_AkYK;EA5&(DKW+OMEXO~u22KNu27p_KeKUHXm{uU zzqc@?7tKP3OcY1Q3YaH(u|4CRYEjA7vF|?d{_Tv#=M^JkSaL22Hn8uVD_6FJm}FXt zgJR;{BMLvemb?3!U$Z+VUVq{{k8SUym)=0>%S=9=i-G$U1uH&b4|8t3QQEMfal?iU z7|_v0_n9CdQSf0lXlJ3C28VfYh%pQutYue*5A*_+ zL;lb}X)O308&bo=bc}q!(y`L;m`Yw^$AY2X)r z@NmrF2P`-+>A)5KG|#7l_Fm@}PBliJq;Hd-hF*{Z3T4Hi6gmqq6fX!u@Sbn86T|(z z!Hd}5f&QMr*B|iXk(huT@}tp~83tMW*|}3V=#-|URsE~_AL(z1)<^3jX-N=pS-~n4 zH-|Tew+s?jk1N?sE|blS4vm&ZOD3kiL5H#+q)E7s>Feq5?(4yjqaqKrlv+!zMgnw` ziHxD>li!~F*2K3-X2OEE3ii36lSO~F$GL{_SZG&oLvOx!*Ep@2q;&tT-u!sO_^$ET zSVJ#!pqsVNT=$V$Q~#+}7i)j+qqV%g3U00FSfg8O(cH)Ex7_Dli2^={Vjbh|n3eYp z(wbzcCVAdIv(~`VU#f%;RDx;ZB?e;%hC*ySuf~9o6IEX2F=dY2ch}QN26IMKp5O@T zhoAa+{7Ikx`HHQ{+{`FGk}p+$=I0F8-hblZgZ5<6ed1?q@!x;$XTp2pC-gEq%6{m{ zp>19-newAQFXVR6+0F6}@#uods&pEoV;*f#%p@?$@}D|Typ8IV+R9uRjrM#i1aZ_nZWN*}oY;b$0(Io!ZG z7yDZyk=CFy%(fLHGXeWM{+VKNI;gx)e<{CDH${q_l;oXZ?y)D@W|?p)s)ph`^%1NJxlBb4@I-kbB2<(!EImiqe6*LS?O zD{ojdlbC6nZJTX3(?*IOsS{w3?67TlAPhZ1RZIcnc14vr~$eYJVic}>WYQp zJ#kE7x)vjg9f5Yar-ybZ?GruI$~2vvkf+IGlUyN}h(zLvNHD4-vb0DFxojbTe&i#Z zUHQvPoys6}e3xD7_|Sxqkf)r=OMi)J3%`WVpzU|oa&ouWlkVBXmr@y_G?Upvx6*Bi zt>Kxt5Ki~-ctk+yM!Q?^V*K#tnb4oOQN5^V_3qi`@y6Wnz(`lInWQh`=#(&-p6C&~ zr7oE`r?69WQW_Vm34M}Ik;k0txINh&lbg0BwLzoT2sie2_V<%!JIjsJF?B>8*xfbN zKSZ1#RSg$6Z5rOyuZF;|BUjnWxKVLT1}*nCJ22SQ+224yOF7!DcbmQA+N3fC&gdy@ z7wuMiwREr0O}ogWf8nx1Q5Y49+eW8K!;SIg-tJbi-&w_lgfO2FHf`M5y*EBE+&vxM zO@5l;LUKs%lSb;NI)@|V0>C<*)F;gGZar+q^>BN#nKsiFt97U&XAB!tMtWSBpc6zk z(0EhaO&cA|soVd`WfL@#On|(S(&dvn zI@|sS@ay=LD|cI4QT{^NFaw+0Sp2q7I?v9!Qra^3bfArWr<3;UN4#!cqU2rdpO#_&4EAYV@mN!s#cUFDq zwwsn)zPG%+{5TzO*i1Zkk5^F>3mQK5OAwT_cMeC#s28v@#sO4HI-Se#uqYmY)$y@? z@NeLk>^`_J?Vi;U1YRJR9n6E^q~Oe!QT1K{{Y_TpS!O4HbvM zo_Ww-!Nt>2DUJRp%#u)%b)JP~?d~dyG_6F`K{KU^mTGCHrCVZ70PQM!VPZ1!svq3T zotw4}k8j=dJUvKvhBvirYStQQg4{O4dXFw}cO7v@4HyXHHrN=$Nuqnm-#zI}oTq-w zrR~)wE0@xn!ef1tZG{HfK-;2y-GiZYCk+#O_3hjaXY~efO1@?vJ-QUYqQl8FTj^N&7@wFFu(@#zYqhvuU*N zFNM%JEjuEwhi@M^%l8rTT7(x>d}IMn6AC>t8U2ZFBw8+Lds zNNxYdcJQkbQ~;Bd0+q@0PvWCi72VAQ(IoQxqm0SB4|_@{X<8>6W~t+6c-Q`1;|J+t z1Pd(M1OZ2}mvCqyzy;3`UgIAd21A58?Dkm(I7l{lJx!Bop4|T^!paLG51`|7-2;o>wg2C?t~JJus|wrOz1~Zvsu0COBFGCr zKtP-Qg+N3p%RAZ7A2e-1X%HLK(4?eIw0W$(u|4+8cxLRmW6yYA9@`u5W*^yALjy^r zwmjTakSIm8K|+WqYIqbX5h#BUKL*KC#W`~~aaJ%ZTUYna<9c=PIp3Z0o$qGTJGVrS z|1{m-hg@_S&92Neo9^rH+edgAehfGO+V2BG;bnL{Is>Ta6JF-m(mOZ&LNu9<)2m^O zXV_o@x(p7)KpShwVE6X!-;f8YZglx)M75X)hO+6hCUzOH(0cUs^sX&8NBQT0=#}VS zX(V%Fj^9ILNaa?mG5anK7Q$T~ev9Zl7t%(88U6cjNz-z`_<&JOM(s41M%j?nN5B1@ zZ?K*0iC?haunXb&rNy(0&$lkt5jIsE<|D=oSW~4hPsg!Mus)pHabTLjU@_wNsV2A& zhXu_h?R3e}+DRo}n9b@lEXSUF@u{D)pRh|8tXE9mZF&YpiRkCk)&@tehS>Pw9Wlh# zd1fb4AdrO>eR_;J;2ayxhGW<|{RakRJ{tWKuZ?MIm`P!pUp+4AO0_02qb@VW6wTTC zjCGp2N8Q6vrRXw1K1oL@U><6ZZ8m1;&|~jDyN%#$Wvzx(IK58G7Lgm8-|$Qpyx^=h zSM6UK0rSD6ntA72oxG#*E771gELyjS)pVeDCVQvwV-yGZk@=U-;%U@V{aOGIXxn&yJ47yS z5QzZzlwQ7@@8;Z-0Bz5uPO_YmheOE$2xEf}|LGahazYEbjj%O;c>#_Ue~qoN z6=@;6FzcUS$JkNnL>7+Y(lJIq`4v*j!VoAH#kooj20^}A42mtKt)HP!F0g6BPP#dG zX1=E&@;jGWVm-GDhuZ^``L*a^n)NDOv0aSw5_9ZyMJ$S9B@e}%1Aa0r&Z~eWd#u}R zx5Lnxhn4aJ|M5P^(l5Ue&Hi=yLMpQ*`GM*C-?wo8mX9R1KA89v?!58-#K#k#OH3v9 zC!R|*5-W+960axzm`o-=l-!n_Ozuq{O3saJ9oaT=WJDiX9=Sa7=aKjBd;7kBkCM@6 zMqeDgI{F^@CXvYNIcM-{EWC?}IZ6c`>8oR|0cE~qry=9Y}ZiyOh8dCCsyU!nnE?j0aY%ygIRd8=n# zGyn=>9HCr)x>K@aENf9@;MN-`N7S$|FNLvRNh^%jQ!DJ67YEslMwJSP-cXt_A7i?( zn-MloGkNng!+g_IZgw~$VK@*JJrJVyswF$*Lk=o_?j#vH=a z%4s3R;_w<}CAPD}*lUE7)9+!M*SSj4UADPlR_|q-!$p?~>NT+cOHprdLL#TAn{h~Y zNKr?VixhPurg9<~S&w6V|3guaDa)v&!#T5@q|PbooF!M`3KW9qOW`IGm>>pH*y0SZ zl86j2VSawTx3Cnp&zOPMRlO3JD5Lf$dzKD9JI1mVF|Wo$75*cVt$4Y`?Abzx!)cn2 zWxsLz4)b)q*fr*9M8+1+beA%i!3d^>FPW$yOR}U$vBI6ABl#Ew?M0SHx*&9xeQX^d zCaEJEq(;v@M;b9Ni-twQe->@3R)s|^A{;5qw5r&Y-x#FwgY-XaG=&2cQ4|c${NkWME+617ZmV5MW|p1i~pm%mU^y z000W60LK6Tc${NkW@2ERz`)AD!RW)7#=yYf4yC^`NHVlAFfcK&ax%aGqW}W}1f;p9 zFgP$MJ}_l`@c#jW9urgzn3&4g&7c5;i~#9o3oifwc$~G6g@FxHjfdmnZix5Il6NZLx+OW|slisqn{t29FsAzaavt`LSj-@%1f%= zke-p*(A8MhoEs&{Jmdnub+Q_DljP66%wir(}cI#Iy(Pi|M&O=W_w0ic${Nk zWME(b;`EI5SL69@zA|t#F@V7JuOgdZ^#9uo3{0wEaSjGB1prI&3o`%!c${NkWME)o z00KQGhX1$!-)2%}U}QiAOaNR31QGxMc$__tJxhXN7=Q1bE=dAVYZY#7gsd3%elJl z*xlgRP^C?V3TgJZ7I7L}YPUyj#*e#tJewH&*}v z002+`0F?j$c$|%ou?@m76hto}L_$y^MCpQp#*yPRkO8Qv*dQd9P=W$YW}s&h#$W)( zU;qj(;Uj@WfhFrd-#>q!0gP~lhjw`wVk+z-gca^#j+1ae&sDfjz6*yKdSg1hU_fQk z-D=tA{F!hMYb=EWdM?6!@=Z7-KTyD7%dL@PhX#8p2cU3Ur}JI2FL?Osyu}qNlsI;^ zrX_>oHzF9;l>7JKSI;7$9@DGIhP2?osywc$I?t4ij7rr<1fFc7W23E!>HH`2hJk9T ru8XPoL>mp8tUkD^RB>efn)3v5!9{!kc${NkW +

  • +
    + +
  • Character mapping

      @@ -1194,6 +1198,10 @@
      +
    • +
      + +