diff --git a/interface/resources/images/hifi-logo.svg b/interface/resources/images/hifi-logo.svg
new file mode 100644
index 0000000000..e5d66d8f18
--- /dev/null
+++ b/interface/resources/images/hifi-logo.svg
@@ -0,0 +1,58 @@
+
+
+
+
diff --git a/interface/resources/images/login.svg b/interface/resources/images/login.svg
new file mode 100644
index 0000000000..f45097b8e3
--- /dev/null
+++ b/interface/resources/images/login.svg
@@ -0,0 +1,17 @@
+
+
+
+
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index ff3c6a1f43..00ed3c8eef 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -40,6 +40,7 @@
#include "ui/InfoView.h"
#include "ui/MetavoxelEditor.h"
#include "ui/ModelsBrowser.h"
+#include "ui/LoginDialog.h"
Menu* Menu::_instance = NULL;
@@ -817,38 +818,9 @@ const int QLINE_MINIMUM_WIDTH = 400;
const float DIALOG_RATIO_OF_WINDOW = 0.30f;
void Menu::loginForCurrentDomain() {
- QDialog loginDialog(Application::getInstance()->getWindow());
- loginDialog.setWindowTitle("Login");
-
- QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom);
- loginDialog.setLayout(layout);
- loginDialog.setWindowFlags(Qt::Sheet);
-
- QFormLayout* form = new QFormLayout();
- layout->addLayout(form, 1);
-
- QLineEdit* loginLineEdit = new QLineEdit();
- loginLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
- form->addRow("Login:", loginLineEdit);
-
- QLineEdit* passwordLineEdit = new QLineEdit();
- passwordLineEdit->setMinimumWidth(QLINE_MINIMUM_WIDTH);
- passwordLineEdit->setEchoMode(QLineEdit::Password);
- form->addRow("Password:", passwordLineEdit);
-
- QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
- loginDialog.connect(buttons, SIGNAL(accepted()), SLOT(accept()));
- loginDialog.connect(buttons, SIGNAL(rejected()), SLOT(reject()));
- layout->addWidget(buttons);
-
- int dialogReturn = loginDialog.exec();
-
- if (dialogReturn == QDialog::Accepted && !loginLineEdit->text().isEmpty() && !passwordLineEdit->text().isEmpty()) {
- // attempt to get an access token given this username and password
- AccountManager::getInstance().requestAccessToken(loginLineEdit->text(), passwordLineEdit->text());
- }
-
- sendFakeEnterEvent();
+ LoginDialog* loginDialog = new LoginDialog(Application::getInstance()->getWindow());
+ loginDialog->show();
+ loginDialog->resizeAndPosition(false);
}
void Menu::editPreferences() {
diff --git a/interface/src/ui/FramelessDialog.cpp b/interface/src/ui/FramelessDialog.cpp
index 4919e99db6..71c58078d2 100644
--- a/interface/src/ui/FramelessDialog.cpp
+++ b/interface/src/ui/FramelessDialog.cpp
@@ -19,7 +19,8 @@ FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Positio
_isResizing(false),
_resizeInitialWidth(0),
_selfHidden(false),
- _position(position) {
+ _position(position),
+ _hideOnBlur(true) {
setAttribute(Qt::WA_DeleteOnClose);
@@ -43,7 +44,7 @@ bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
}
break;
case QEvent::WindowStateChange:
- if (parentWidget()->isMinimized()) {
+ if (_hideOnBlur && parentWidget()->isMinimized()) {
if (isVisible()) {
_selfHidden = true;
setHidden(true);
@@ -55,7 +56,7 @@ bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
break;
case QEvent::ApplicationDeactivate:
// hide on minimize and focus lost
- if (isVisible()) {
+ if (_hideOnBlur && isVisible()) {
_selfHidden = true;
setHidden(true);
}
@@ -84,18 +85,23 @@ void FramelessDialog::setStyleSheetFile(const QString& fileName) {
void FramelessDialog::showEvent(QShowEvent* event) {
resizeAndPosition();
+ QDialog::showEvent(event);
}
void FramelessDialog::resizeAndPosition(bool resizeParent) {
- // keep full app height
- setFixedHeight(parentWidget()->size().height());
+ // keep full app height or width depending on position
+ if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
+ setFixedHeight(parentWidget()->size().height());
+ } else {
+ setFixedWidth(parentWidget()->size().width());
+ }
// resize parrent if width is smaller than this dialog
if (resizeParent && parentWidget()->size().width() < size().width()) {
parentWidget()->resize(size().width(), parentWidget()->size().height());
}
- if (_position == POSITION_LEFT) {
+ if (_position == POSITION_LEFT || _position == POSITION_TOP) {
// move to upper left corner
move(parentWidget()->geometry().topLeft());
} else if (_position == POSITION_RIGHT) {
@@ -104,16 +110,26 @@ void FramelessDialog::resizeAndPosition(bool resizeParent) {
pos.setX(pos.x() - size().width());
move(pos);
}
+ repaint();
}
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
if (mouseEvent->button() == Qt::LeftButton) {
- bool hitLeft = _position == POSITION_LEFT && abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH;
- bool hitRight = _position == POSITION_RIGHT && mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH;
- if (hitLeft || hitRight) {
- _isResizing = true;
- _resizeInitialWidth = size().width();
- QApplication::setOverrideCursor(Qt::SizeHorCursor);
+ if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
+ bool hitLeft = (_position == POSITION_LEFT) && (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH);
+ bool hitRight = (_position == POSITION_RIGHT) && (mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH);
+ if (hitLeft || hitRight) {
+ _isResizing = true;
+ _resizeInitialWidth = size().width();
+ QApplication::setOverrideCursor(Qt::SizeHorCursor);
+ }
+ } else {
+ bool hitTop = (_position == POSITION_TOP) && (abs(mouseEvent->pos().y() - size().height()) < RESIZE_HANDLE_WIDTH);
+ if (hitTop) {
+ _isResizing = true;
+ _resizeInitialWidth = size().height();
+ QApplication::setOverrideCursor(Qt::SizeHorCursor);
+ }
}
}
}
@@ -133,6 +149,8 @@ void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
resizeAndPosition();
_resizeInitialWidth = size().width();
setUpdatesEnabled(true);
+ } else if (_position == POSITION_TOP) {
+ resize(size().width(), mouseEvent->pos().y());
}
}
}
diff --git a/interface/src/ui/FramelessDialog.h b/interface/src/ui/FramelessDialog.h
index 828602a5db..2265f3c9e4 100644
--- a/interface/src/ui/FramelessDialog.h
+++ b/interface/src/ui/FramelessDialog.h
@@ -17,12 +17,15 @@
class FramelessDialog : public QDialog {
Q_OBJECT
-
-public:
- enum Position { POSITION_LEFT, POSITION_RIGHT };
- FramelessDialog(QWidget* parent = 0, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
+public:
+ enum Position { POSITION_LEFT, POSITION_RIGHT, POSITION_TOP };
+
+ FramelessDialog(QWidget* parent, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
void setStyleSheetFile(const QString& fileName);
+ void setHideOnBlur(bool hideOnBlur) { _hideOnBlur = hideOnBlur; };
+ bool getHideOnBlur() { return _hideOnBlur; };
+ void resizeAndPosition(bool resizeParent = true);
protected:
virtual void mouseMoveEvent(QMouseEvent* mouseEvent);
@@ -33,12 +36,11 @@ protected:
bool eventFilter(QObject* sender, QEvent* event);
private:
- void resizeAndPosition(bool resizeParent = true);
-
bool _isResizing;
int _resizeInitialWidth;
bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization)
Position _position;
+ bool _hideOnBlur;
};
diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp
new file mode 100644
index 0000000000..993f355c47
--- /dev/null
+++ b/interface/src/ui/LoginDialog.cpp
@@ -0,0 +1,91 @@
+//
+// LoginDialog.cpp
+// interface/src/ui
+//
+// Created by Ryan Huffman on 4/23/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+
+#include
+#include
+#include
+
+#include "Application.h"
+#include "Menu.h"
+#include "AccountManager.h"
+#include "ui_loginDialog.h"
+
+#include "LoginDialog.h"
+
+const QString FORGOT_PASSWORD_URL = "https://data-web.highfidelity.io/password/new";
+
+LoginDialog::LoginDialog(QWidget* parent) :
+ FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
+ _ui(new Ui::LoginDialog) {
+
+ _ui->setupUi(this);
+ _ui->errorLabel->hide();
+ _ui->emailLineEdit->setFocus();
+ _ui->logoLabel->setPixmap(QPixmap(Application::resourcesPath() + "images/hifi-logo.svg"));
+ _ui->loginButton->setIcon(QIcon(Application::resourcesPath() + "images/login.svg"));
+ _ui->infoLabel->setVisible(false);
+ _ui->errorLabel->setVisible(false);
+
+ setModal(true);
+ setHideOnBlur(false);
+
+ connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
+ this, &LoginDialog::handleLoginCompleted);
+ connect(&AccountManager::getInstance(), &AccountManager::loginFailed,
+ this, &LoginDialog::handleLoginFailed);
+ connect(_ui->loginButton, &QPushButton::clicked,
+ this, &LoginDialog::handleLoginClicked);
+ connect(_ui->closeButton, &QPushButton::clicked,
+ this, &LoginDialog::close);
+};
+
+LoginDialog::~LoginDialog() {
+ delete _ui;
+};
+
+void LoginDialog::handleLoginCompleted(const QUrl& authURL) {
+ _ui->infoLabel->setVisible(false);
+ _ui->errorLabel->setVisible(false);
+ close();
+};
+
+void LoginDialog::handleLoginFailed() {
+ _ui->infoLabel->setVisible(false);
+ _ui->errorLabel->setVisible(true);
+
+ _ui->errorLabel->show();
+ _ui->loginArea->setDisabled(false);
+
+ // Move focus to password and select the entire line
+ _ui->passwordLineEdit->setFocus();
+ _ui->passwordLineEdit->setSelection(0, _ui->emailLineEdit->maxLength());
+};
+
+void LoginDialog::handleLoginClicked() {
+ // If the email or password inputs are empty, move focus to them, otherwise attempt to login.
+ if (_ui->emailLineEdit->text().isEmpty()) {
+ _ui->emailLineEdit->setFocus();
+ } else if (_ui->passwordLineEdit->text().isEmpty()) {
+ _ui->passwordLineEdit->setFocus();
+ } else {
+ _ui->infoLabel->setVisible(true);
+ _ui->errorLabel->setVisible(false);
+
+ _ui->loginArea->setDisabled(true);
+ AccountManager::getInstance().requestAccessToken(_ui->emailLineEdit->text(), _ui->passwordLineEdit->text());
+ }
+};
+
+void LoginDialog::moveEvent(QMoveEvent* event) {
+ // Modal dialogs seemed to get repositioned automatically. Combat this by moving the window if needed.
+ resizeAndPosition();
+};
diff --git a/interface/src/ui/LoginDialog.h b/interface/src/ui/LoginDialog.h
new file mode 100644
index 0000000000..b4aad06614
--- /dev/null
+++ b/interface/src/ui/LoginDialog.h
@@ -0,0 +1,42 @@
+//
+// LoginDialog.h
+// interface/src/ui
+//
+// Created by Ryan Huffman on 4/23/14.
+// Copyright 2014 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_LoginDialog_h
+#define hifi_LoginDialog_h
+
+#include
+#include "FramelessDialog.h"
+
+namespace Ui {
+ class LoginDialog;
+}
+
+class LoginDialog : public FramelessDialog {
+ Q_OBJECT
+
+public:
+ LoginDialog(QWidget* parent);
+ ~LoginDialog();
+
+public slots:
+ void handleLoginClicked();
+ void handleLoginCompleted(const QUrl& authURL);
+ void handleLoginFailed();
+
+protected:
+ void moveEvent(QMoveEvent* event);
+
+private:
+ Ui::LoginDialog* _ui;
+
+};
+
+#endif // hifi_LoginDialog_h
diff --git a/interface/ui/loginDialog.ui b/interface/ui/loginDialog.ui
new file mode 100644
index 0000000000..d54b3c833f
--- /dev/null
+++ b/interface/ui/loginDialog.ui
@@ -0,0 +1,491 @@
+
+
+ LoginDialog
+
+
+
+ 0
+ 0
+ 1003
+ 130
+
+
+
+
+ 0
+ 130
+
+
+
+ Dialog
+
+
+ font-family: Helvetica, Arial, sans-serif;
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 30
+
+
+
+
+ 5
+
+
+ 0
+
+
+ 10
+
+
+ 10
+
+
+ 0
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 825
+ 0
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ Helvetica,Arial,sans-serif
+ 17
+
+
+
+ Authenticating...
+
+
+
+ -
+
+
+ true
+
+
+
+ Helvetica,Arial,sans-serif
+ 17
+
+
+
+ color: #992800;
+
+
+ <style type="text/css">
+ a { text-decoration: none; color: #267077;}
+</style>
+Invalid username or password. <a href="https://data-web.highfidelity.io/password/new">Recover?</a>
+
+
+ Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 16
+ 16
+
+
+
+
+ 16
+ 16
+
+
+
+ SplitHCursor
+
+
+
+
+
+
+
+
+
+ ../resources/images/close.svg../resources/images/close.svg
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ true
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 102
+
+
+
+
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 30
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 12
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 0
+ 0
+
+
+
+
+
+
+ ../resources/images/hifi-logo.png
+
+
+
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 931
+ 60
+
+
+
+ QLineEdit {
+ padding: 10px;
+border-width: 1px; border-style: solid; border-radius: 3px; border-color: #aaa;
+}
+
+
+
+ 12
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
+ 0
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 54
+
+
+
+
+ Helvetica,Arial,sans-serif
+ 20
+
+
+
+ padding-top: 14px;
+
+
+
+
+
+ Username or Email
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 300
+ 54
+
+
+
+
+ Helvetica,Arial,sans-serif
+ 20
+
+
+
+ padding-top: 14px;
+
+
+
+
+
+ QLineEdit::Password
+
+
+ Password
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 141
+ 54
+
+
+
+
+ Helvetica,Arial,sans-serif
+ 20
+
+
+
+ PointingHandCursor
+
+
+
+background: #0e7077;
+color: #e7eeee;
+border-radius: 4px; padding-top: 1px;
+
+
+ Login
+
+
+
+ ../resources/images/login.svg../resources/images/login.svg
+
+
+
+ 32
+ 32
+
+
+
+ true
+
+
+ true
+
+
+
+ -
+
+
+
+ Helvetica,Arial,sans-serif
+ 17
+
+
+
+ <style type="text/css">
+ a { text-decoration: none; color: #267077;}
+</style>
+<a href="https://data-web.highfidelity.io/password/new">Recover password?</a>
+
+
+ true
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libraries/networking/src/AccountManager.cpp b/libraries/networking/src/AccountManager.cpp
index 278923026d..547768ec48 100644
--- a/libraries/networking/src/AccountManager.cpp
+++ b/libraries/networking/src/AccountManager.cpp
@@ -337,6 +337,7 @@ void AccountManager::requestFinished() {
} else {
// TODO: error handling
qDebug() << "Error in response for password grant -" << rootObject["error_description"].toString();
+ emit loginFailed();
}
}
diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h
index cb76786f4e..8df75195cf 100644
--- a/libraries/networking/src/AccountManager.h
+++ b/libraries/networking/src/AccountManager.h
@@ -71,6 +71,7 @@ signals:
void usernameChanged(const QString& username);
void accessTokenChanged();
void loginComplete(const QUrl& authURL);
+ void loginFailed();
void logoutComplete();
private slots:
void processReply();