[gnome-shell] Login: add a spinner for better process indication



commit 59a7fdd2c9387ee4b9b6d2b92375e57496ff718f
Author: StÃphane DÃmurget <stephane demurget free fr>
Date:   Sun Nov 18 22:49:54 2012 +0100

    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

 data/theme/gnome-shell.css |    4 ++
 js/gdm/loginDialog.js      |  104 ++++++++++++++++++++++++++++++++++----------
 js/ui/unlockDialog.js      |   66 ++++++++++++++++++++++++----
 3 files changed, 142 insertions(+), 32 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index e99acb0..4890f2c 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -2213,6 +2213,10 @@ StScrollBar StButton#vhandle:active {
     height: .75em;
 }
 
+.login-dialog .modal-dialog-button-box {
+    spacing: 3px;
+}
+
 .login-dialog .modal-dialog-button {
     border-radius: 5px;
     padding: 3px 18px;
diff --git a/js/gdm/loginDialog.js b/js/gdm/loginDialog.js
index c72ce47..1710322 100644
--- a/js/gdm/loginDialog.js
+++ b/js/gdm/loginDialog.js
@@ -39,6 +39,7 @@ const GdmUtil = imports.gdm.util;
 const Lightbox = imports.ui.lightbox;
 const Main = imports.ui.main;
 const ModalDialog = imports.ui.modalDialog;
+const Panel = imports.ui.panel;
 const PanelMenu = imports.ui.panelMenu;
 const Tweener = imports.ui.tweener;
 const UserMenu = imports.ui.userMenu;
@@ -48,6 +49,10 @@ const _SCROLL_ANIMATION_TIME = 0.5;
 const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0;
 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;
 
 function _smoothlyResizeActor(actor, width, height) {
@@ -760,6 +765,7 @@ const LoginDialog = new Lang.Class({
         this._promptBox.add(this._promptLoginHint);
 
         this._signInButton = null;
+        this._workSpinner = null;
 
         this._sessionList = new SessionList();
         this._sessionList.connect('session-activated',
@@ -855,6 +861,7 @@ const LoginDialog = new Lang.Class({
         this._promptEntry.text = '';
 
         this._updateSensitivity(true);
+        this._setWorking(false);
     },
 
     _onDefaultSessionChanged: function(client, sessionId) {
@@ -927,34 +934,12 @@ const LoginDialog = new Lang.Class({
     _showPrompt: function(forSecret) {
         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() {
                          return this._fadeInPrompt();
                      },
 
                      function() {
-                         this.setButtons(buttons);
-                         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);
-                                                                    }));
+                         this._prepareDialog(forSecret, hold);
                      },
 
                      hold];
@@ -964,6 +949,49 @@ const LoginDialog = new Lang.Class({
         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) {
         this._promptEntry.reactive = sensitive;
         this._promptEntry.clutter_text.editable = sensitive;
@@ -987,6 +1015,8 @@ const LoginDialog = new Lang.Class({
         }
 
         let tasks = [function() {
+                         this._setWorking(false);
+
                          return GdmUtil.fadeOutActor(this._promptBox);
                      },
 
@@ -996,6 +1026,8 @@ const LoginDialog = new Lang.Class({
                          this._updateSensitivity(true);
                          this._promptEntry.set_text('');
 
+                         this.clearButtons();
+                         this._workSpinner = null;
                          this._signInButton = null;
                      }];
 
@@ -1004,6 +1036,31 @@ const LoginDialog = new Lang.Class({
         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) {
         this._promptLabel.set_text(question);
 
@@ -1017,6 +1074,7 @@ const LoginDialog = new Lang.Class({
                      function() {
                          let text = this._promptEntry.get_text();
                          this._updateSensitivity(false);
+                         this._setWorking(true);
                          this._userVerifier.answerQuery(serviceName, text);
                      }];
 
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
index 548bbe7..5cdce92 100644
--- a/js/ui/unlockDialog.js
+++ b/js/ui/unlockDialog.js
@@ -14,12 +14,14 @@ const St = imports.gi.St;
 
 const Main = imports.ui.main;
 const ModalDialog = imports.ui.modalDialog;
+const Panel = imports.ui.panel;
 const ShellEntry = imports.ui.shellEntry;
 const Tweener = imports.ui.tweener;
 const UserMenu = imports.ui.userMenu;
 
 const Batch = imports.gdm.batch;
 const GdmUtil = imports.gdm.util;
+const LoginDialog = imports.gdm.loginDialog;
 
 // The timeout before going back automatically to the lock screen (in seconds)
 const IDLE_TIMEOUT = 2 * 60;
@@ -168,13 +170,33 @@ const UnlockDialog = new Lang.Class({
         this._promptLoginHint.hide();
         this.contentLayout.add_actor(this._promptLoginHint);
 
-        let cancelButton = { label: _("Cancel"),
-                             action: Lang.bind(this, this._escape),
-                             key: Clutter.KEY_Escape };
-        this._okButton = { label: _("Unlock"),
-                           action: Lang.bind(this, this._doUnlock),
-                           default: true };
-        this.setButtons([cancelButton, this._okButton]);
+        this._workSpinner = new Panel.AnimatedIcon('process-working.svg', LoginDialog.WORK_SPINNER_ICON_SIZE);
+        this._workSpinner.actor.opacity = 0;
+        this._workSpinner.actor.show();
+
+        this.buttonLayout.visible = true;
+        this.addButton({ label: _("Cancel"),
+                         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"),
                                             style_class: 'login-dialog-not-listed-label' });
@@ -214,8 +236,30 @@ const UnlockDialog = new Lang.Class({
     },
 
     _updateOkButtonSensitivity: function(sensitive) {
-        this._okButton.button.reactive = sensitive;
-        this._okButton.button.can_focus = sensitive;
+        this._okButton.reactive = 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) {
@@ -248,6 +292,7 @@ const UnlockDialog = new Lang.Class({
 
         this._currentQuery = serviceName;
         this._updateSensitivity(true);
+        this._setWorking(false);
     },
 
     _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
             this._firstQuestionAnswer = this._promptEntry.text;
             this._updateSensitivity(false);
+            this._setWorking(true);
             return;
         }
 
@@ -276,6 +322,7 @@ const UnlockDialog = new Lang.Class({
         this._currentQuery = null;
 
         this._updateSensitivity(false);
+        this._setWorking(true);
 
         this._userVerifier.answerQuery(query, this._promptEntry.text);
     },
@@ -296,6 +343,7 @@ const UnlockDialog = new Lang.Class({
         this._promptEntry.text = '';
 
         this._updateSensitivity(false);
+        this._setWorking(false);
     },
 
     _escape: function() {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]