Login: add a spinner for better process indication

We need to do a better job of indicating login process. This can
sometimes take a few seconds (particularly if you get your password
wrong): we need to give better feedback of what's going on.

This adds a spinner next to the login button if the authorization takes
some time.

https://bugzilla.gnome.org/show_bug.cgi?id=687113
This commit is contained in:
Stéphane Démurget 2012-11-18 22:49:54 +01:00
parent c3cab28c9b
commit 59a7fdd2c9
3 changed files with 142 additions and 32 deletions

View File

@ -2213,6 +2213,10 @@ StScrollBar StButton#vhandle:active {
height: .75em; height: .75em;
} }
.login-dialog .modal-dialog-button-box {
spacing: 3px;
}
.login-dialog .modal-dialog-button { .login-dialog .modal-dialog-button {
border-radius: 5px; border-radius: 5px;
padding: 3px 18px; padding: 3px 18px;

View File

@ -39,6 +39,7 @@ const GdmUtil = imports.gdm.util;
const Lightbox = imports.ui.lightbox; const Lightbox = imports.ui.lightbox;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const PanelMenu = imports.ui.panelMenu; const PanelMenu = imports.ui.panelMenu;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu; const UserMenu = imports.ui.userMenu;
@ -48,6 +49,10 @@ const _SCROLL_ANIMATION_TIME = 0.5;
const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
const _LOGO_ICON_HEIGHT = 16; const _LOGO_ICON_HEIGHT = 16;
const WORK_SPINNER_ICON_SIZE = 24;
const WORK_SPINNER_ANIMATION_DELAY = 1.0;
const WORK_SPINNER_ANIMATION_TIME = 0.3;
let _loginDialog = null; let _loginDialog = null;
function _smoothlyResizeActor(actor, width, height) { function _smoothlyResizeActor(actor, width, height) {
@ -760,6 +765,7 @@ const LoginDialog = new Lang.Class({
this._promptBox.add(this._promptLoginHint); this._promptBox.add(this._promptLoginHint);
this._signInButton = null; this._signInButton = null;
this._workSpinner = null;
this._sessionList = new SessionList(); this._sessionList = new SessionList();
this._sessionList.connect('session-activated', this._sessionList.connect('session-activated',
@ -855,6 +861,7 @@ const LoginDialog = new Lang.Class({
this._promptEntry.text = ''; this._promptEntry.text = '';
this._updateSensitivity(true); this._updateSensitivity(true);
this._setWorking(false);
}, },
_onDefaultSessionChanged: function(client, sessionId) { _onDefaultSessionChanged: function(client, sessionId) {
@ -927,34 +934,12 @@ const LoginDialog = new Lang.Class({
_showPrompt: function(forSecret) { _showPrompt: function(forSecret) {
let hold = new Batch.Hold(); let hold = new Batch.Hold();
let cancelButtonInfo = { action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape };
let okButtonInfo = { action: Lang.bind(this, function() {
hold.release();
}),
label: forSecret ? C_("button", "Sign In") : _("Next"),
default: true };
let buttons = [];
if (!this._disableUserList || this._verifyingUser)
buttons.push(cancelButtonInfo);
buttons.push(okButtonInfo);
let tasks = [function() { let tasks = [function() {
return this._fadeInPrompt(); return this._fadeInPrompt();
}, },
function() { function() {
this.setButtons(buttons); this._prepareDialog(forSecret, hold);
this._signInButton = okButtonInfo.button;
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
}, },
hold]; hold];
@ -964,6 +949,49 @@ const LoginDialog = new Lang.Class({
return batch.run(); return batch.run();
}, },
_prepareDialog: function(forSecret, hold) {
this._workSpinner = new Panel.AnimatedIcon('process-working.svg', WORK_SPINNER_ICON_SIZE);
this._workSpinner.actor.opacity = 0;
this._workSpinner.actor.show();
this.buttonLayout.visible = true;
this.clearButtons();
if (!this._disableUserList || this._verifyingUser)
this.addButton({ action: Lang.bind(this, this.cancel),
label: _("Cancel"),
key: Clutter.Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._signInButton = this.addButton({ action: Lang.bind(this, function() {
hold.release();
}),
label: forSecret ? C_("button", "Sign In") : _("Next"),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
this._promptEntryTextChangedId =
this._promptEntry.clutter_text.connect('text-changed',
Lang.bind(this, function() {
this._updateSignInButtonSensitivity(this._promptEntry.text.length > 0);
}));
},
_updateSensitivity: function(sensitive) { _updateSensitivity: function(sensitive) {
this._promptEntry.reactive = sensitive; this._promptEntry.reactive = sensitive;
this._promptEntry.clutter_text.editable = sensitive; this._promptEntry.clutter_text.editable = sensitive;
@ -987,6 +1015,8 @@ const LoginDialog = new Lang.Class({
} }
let tasks = [function() { let tasks = [function() {
this._setWorking(false);
return GdmUtil.fadeOutActor(this._promptBox); return GdmUtil.fadeOutActor(this._promptBox);
}, },
@ -996,6 +1026,8 @@ const LoginDialog = new Lang.Class({
this._updateSensitivity(true); this._updateSensitivity(true);
this._promptEntry.set_text(''); this._promptEntry.set_text('');
this.clearButtons();
this._workSpinner = null;
this._signInButton = null; this._signInButton = null;
}]; }];
@ -1004,6 +1036,31 @@ const LoginDialog = new Lang.Class({
return batch.run(); return batch.run();
}, },
_setWorking: function(working) {
if (!this._workSpinner)
return;
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: WORK_SPINNER_ANIMATION_DELAY,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
this._workSpinner.stop();
}
});
}
},
_askQuestion: function(verifier, serviceName, question, passwordChar) { _askQuestion: function(verifier, serviceName, question, passwordChar) {
this._promptLabel.set_text(question); this._promptLabel.set_text(question);
@ -1017,6 +1074,7 @@ const LoginDialog = new Lang.Class({
function() { function() {
let text = this._promptEntry.get_text(); let text = this._promptEntry.get_text();
this._updateSensitivity(false); this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(serviceName, text); this._userVerifier.answerQuery(serviceName, text);
}]; }];

View File

@ -14,12 +14,14 @@ const St = imports.gi.St;
const Main = imports.ui.main; const Main = imports.ui.main;
const ModalDialog = imports.ui.modalDialog; const ModalDialog = imports.ui.modalDialog;
const Panel = imports.ui.panel;
const ShellEntry = imports.ui.shellEntry; const ShellEntry = imports.ui.shellEntry;
const Tweener = imports.ui.tweener; const Tweener = imports.ui.tweener;
const UserMenu = imports.ui.userMenu; const UserMenu = imports.ui.userMenu;
const Batch = imports.gdm.batch; const Batch = imports.gdm.batch;
const GdmUtil = imports.gdm.util; const GdmUtil = imports.gdm.util;
const LoginDialog = imports.gdm.loginDialog;
// The timeout before going back automatically to the lock screen (in seconds) // The timeout before going back automatically to the lock screen (in seconds)
const IDLE_TIMEOUT = 2 * 60; const IDLE_TIMEOUT = 2 * 60;
@ -168,13 +170,33 @@ const UnlockDialog = new Lang.Class({
this._promptLoginHint.hide(); this._promptLoginHint.hide();
this.contentLayout.add_actor(this._promptLoginHint); this.contentLayout.add_actor(this._promptLoginHint);
let cancelButton = { label: _("Cancel"), this._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
action: Lang.bind(this, this._escape), this._workSpinner.actor.opacity = 0;
key: Clutter.KEY_Escape }; this._workSpinner.actor.show();
this._okButton = { label: _("Unlock"),
action: Lang.bind(this, this._doUnlock), this.buttonLayout.visible = true;
default: true }; this.addButton({ label: _("Cancel"),
this.setButtons([cancelButton, this._okButton]); action: Lang.bind(this, this._escape),
key: Clutter.KEY_Escape },
{ expand: true,
x_fill: false,
y_fill: false,
x_align: St.Align.START,
y_align: St.Align.MIDDLE });
this.buttonLayout.add(this._workSpinner.actor,
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
this._okButton = this.addButton({ label: _("Unlock"),
action: Lang.bind(this, this._doUnlock),
default: true },
{ expand: false,
x_fill: false,
y_fill: false,
x_align: St.Align.END,
y_align: St.Align.MIDDLE });
let otherUserLabel = new St.Label({ text: _("Log in as another user"), let otherUserLabel = new St.Label({ text: _("Log in as another user"),
style_class: 'login-dialog-not-listed-label' }); style_class: 'login-dialog-not-listed-label' });
@ -214,8 +236,30 @@ const UnlockDialog = new Lang.Class({
}, },
_updateOkButtonSensitivity: function(sensitive) { _updateOkButtonSensitivity: function(sensitive) {
this._okButton.button.reactive = sensitive; this._okButton.reactive = sensitive;
this._okButton.button.can_focus = sensitive; this._okButton.can_focus = sensitive;
},
_setWorking: function(working) {
if (working) {
this._workSpinner.play();
Tweener.addTween(this._workSpinner.actor,
{ opacity: 255,
delay: LoginDialog.WORK_SPINNER_ANIMATION_DELAY,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear'
});
} else {
Tweener.addTween(this._workSpinner.actor,
{ opacity: 0,
time: LoginDialog.WORK_SPINNER_ANIMATION_TIME,
transition: 'linear',
onCompleteScope: this,
onComplete: function() {
this._workSpinner.stop();
}
});
}
}, },
_showMessage: function(userVerifier, message, styleClass) { _showMessage: function(userVerifier, message, styleClass) {
@ -248,6 +292,7 @@ const UnlockDialog = new Lang.Class({
this._currentQuery = serviceName; this._currentQuery = serviceName;
this._updateSensitivity(true); this._updateSensitivity(true);
this._setWorking(false);
}, },
_showLoginHint: function(verifier, message) { _showLoginHint: function(verifier, message) {
@ -266,6 +311,7 @@ const UnlockDialog = new Lang.Class({
// the actual reply to GDM will be sent as soon as asked // the actual reply to GDM will be sent as soon as asked
this._firstQuestionAnswer = this._promptEntry.text; this._firstQuestionAnswer = this._promptEntry.text;
this._updateSensitivity(false); this._updateSensitivity(false);
this._setWorking(true);
return; return;
} }
@ -276,6 +322,7 @@ const UnlockDialog = new Lang.Class({
this._currentQuery = null; this._currentQuery = null;
this._updateSensitivity(false); this._updateSensitivity(false);
this._setWorking(true);
this._userVerifier.answerQuery(query, this._promptEntry.text); this._userVerifier.answerQuery(query, this._promptEntry.text);
}, },
@ -296,6 +343,7 @@ const UnlockDialog = new Lang.Class({
this._promptEntry.text = ''; this._promptEntry.text = '';
this._updateSensitivity(false); this._updateSensitivity(false);
this._setWorking(false);
}, },
_escape: function() { _escape: function() {