[gnome-shell] ScreenShield: implement curtain design



commit c0652bcd8f570cf98c4fb144bc81ed48c441dbde
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Thu May 24 22:47:48 2012 +0200

    ScreenShield: implement curtain design
    
    This separates the screen shield into two main screens. One is
    the lock screen, and it is shown when coming back from idle status
    and when failing authentication. The other is the actual unlock
    dialog.
    Moving from the first to the second is possible by pressing Escape
    or by dragging an arrow on the bottom on the screen.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=619955

 data/theme/gnome-shell.css |   12 +++
 js/ui/layout.js            |   12 ++--
 js/ui/modalDialog.js       |    6 +-
 js/ui/screenShield.js      |  167 +++++++++++++++++++++++++++++++++++--------
 js/ui/unlockDialog.js      |    6 +-
 5 files changed, 161 insertions(+), 42 deletions(-)
---
diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css
index 5881172..e8c0336 100644
--- a/data/theme/gnome-shell.css
+++ b/data/theme/gnome-shell.css
@@ -2194,3 +2194,15 @@ StScrollBar StButton#vhandle:hover
 .unlock-dialog-user-name-container {
     spacing: .4em;
 }
+
+/* Screen shield */
+
+#screenShieldGroup {
+    background-color: #333333;
+}
+
+#screenShieldGroup .arrow {
+    color: #333333;
+    width: 100px;
+    height: 50px;
+}
diff --git a/js/ui/layout.js b/js/ui/layout.js
index 5175006..446b590 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -41,14 +41,9 @@ const LayoutManager = new Lang.Class({
                               Lang.bind(this, this._updatePanelBarriers));
 
         this.screenShieldGroup = new St.Widget({ name: 'screenShieldGroup',
-                                                 visible: false
+                                                 visible: false,
+                                                 clip_to_allocation: true,
                                                });
-        // FIXME: This is going to slow everything down by a big factor
-        // Need a better solution (like a layout manager?)
-        let constraint = new Clutter.BindConstraint({ source: global.stage,
-                                                      coordinate: Clutter.BindCoordinate.ALL
-                                                    });
-        this.screenShieldGroup.add_constraint(constraint);
         this.addChrome(this.screenShieldGroup);
 
         this.trayBox = new St.BoxLayout({ name: 'trayBox' }); 
@@ -161,6 +156,9 @@ const LayoutManager = new Lang.Class({
     },
 
     _updateBoxes: function() {
+        this.screenShieldGroup.set_position(0, 0);
+        this.screenShieldGroup.set_size(global.screen_width, global.screen_height);
+
         this.panelBox.set_position(this.primaryMonitor.x, this.primaryMonitor.y);
         this.panelBox.set_size(this.primaryMonitor.width, -1);
 
diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js
index bdeee73..84fca17 100644
--- a/js/ui/modalDialog.js
+++ b/js/ui/modalDialog.js
@@ -35,7 +35,9 @@ const ModalDialog = new Lang.Class({
 
     _init: function(params) {
         params = Params.parse(params, { shellReactive: false,
-                                        styleClass: null });
+                                        styleClass: null,
+                                        parentActor: Main.uiGroup
+                                      });
 
         this.state = State.CLOSED;
         this._hasModal = false;
@@ -45,7 +47,7 @@ const ModalDialog = new Lang.Class({
                                       x: 0,
                                       y: 0,
                                       accessible_role: Atk.Role.DIALOG });
-        Main.uiGroup.add_actor(this._group);
+        params.parentActor.add_actor(this._group);
 
         let constraint = new Clutter.BindConstraint({ source: global.stage,
                                                       coordinate: Clutter.BindCoordinate.ALL });
diff --git a/js/ui/screenShield.js b/js/ui/screenShield.js
index 060bd02..e852f32 100644
--- a/js/ui/screenShield.js
+++ b/js/ui/screenShield.js
@@ -11,10 +11,16 @@ const GnomeSession = imports.misc.gnomeSession;
 const Lightbox = imports.ui.lightbox;
 const UnlockDialog = imports.ui.unlockDialog;
 const Main = imports.ui.main;
+const Tweener = imports.ui.tweener;
 
 const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver';
 const LOCK_ENABLED_KEY = 'lock-enabled';
 
+const CURTAIN_SLIDE_TIME = 1.2;
+// fraction of screen height the arrow must reach before completing
+// the slide up automatically
+const ARROW_DRAG_TRESHOLD = 0.4;
+
 /**
  * To test screen shield, make sure to kill gnome-screensaver.
  *
@@ -31,6 +37,43 @@ const ScreenShield = new Lang.Class({
     _init: function() {
         this.actor = Main.layoutManager.screenShieldGroup;
 
+        this._lockScreenGroup = new St.Widget({ x_expand: true,
+                                                y_expand: true,
+                                                reactive: true,
+                                                can_focus: true,
+                                                layout_manager: new Clutter.BinLayout()
+                                              });
+        this._lockScreenGroup.connect('key-release-event',
+                                      Lang.bind(this, this._onLockScreenKeyRelease));
+
+        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
+        this._lockScreenGroup.add_actor(this._background);
+
+        // FIXME: build the rest of the lock screen here
+
+        this._arrow = new St.DrawingArea({ style_class: 'arrow',
+                                           reactive: true,
+                                           x_align: Clutter.ActorAlign.CENTER,
+                                           y_align: Clutter.ActorAlign.END,
+                                           // HACK: without these, ClutterBinLayout
+                                           // ignores alignment properties on the actor
+                                           x_expand: true,
+                                           y_expand: true
+                                         });
+        this._arrow.connect('repaint', Lang.bind(this, this._drawArrow));
+        this._lockScreenGroup.add_actor(this._arrow);
+
+        let action = new Clutter.DragAction({ drag_axis: Clutter.DragAxis.Y_AXIS });
+        action.connect('drag-begin', Lang.bind(this, this._onDragBegin));
+        action.connect('drag-end', Lang.bind(this, this._onDragEnd));
+        this._lockScreenGroup.add_action(action);
+
+        this._lockDialogGroup = new St.Widget({ x_expand: true,
+                                                y_expand: true });
+
+        this.actor.add_actor(this._lockDialogGroup);
+        this.actor.add_actor(this._lockScreenGroup);
+
         this._presence = new GnomeSession.Presence(Lang.bind(this, function(proxy, error) {
             if (error) {
                 logError(error, 'Error while reading gnome-session presence');
@@ -50,9 +93,53 @@ const ScreenShield = new Lang.Class({
 
         this._lightbox = new Lightbox.Lightbox(Main.uiGroup,
                                                { inhibitEvents: true, fadeInTime: 10, fadeFactor: 1 });
+    },
 
-        this._background = Meta.BackgroundActor.new_for_screen(global.screen);
-        this.actor.add_actor(this._background);
+    _onLockScreenKeyRelease: function(actor, event) {
+        if (event.get_key_symbol() == Clutter.KEY_Escape) {
+            this._showUnlockDialog();
+            return true;
+        }
+
+        return false;
+    },
+
+    _drawArrow: function() {
+        let cr = this._arrow.get_context();
+        let [w, h] = this._arrow.get_surface_size();
+        let node = this._arrow.get_theme_node();
+
+        Clutter.cairo_set_source_color(cr, node.get_foreground_color());
+
+        cr.moveTo(0, h);
+        cr.lineTo(w/2, 0);
+        cr.lineTo(w, h);
+        cr.fill();
+    },
+
+    _onDragBegin: function() {
+        Tweener.removeTweens(this._lockScreenGroup);
+    },
+
+    _onDragEnd: function(action, actor, eventX, eventY, modifiers) {
+        if (this._lockScreenGroup.y < -(ARROW_DRAG_TRESHOLD * global.stage.height)) {
+            // Complete motion automatically
+            this._showUnlockDialog();
+        } else {
+            // restore the lock screen to its original place
+            // try to use the same speed as the normal animation
+            let h = global.stage.height;
+            let time = CURTAIN_SLIDE_TIME * (-this._lockScreenGroup.y) / h;
+            Tweener.removeTweens(this._lockScreenGroup);
+            Tweener.addTween(this._lockScreenGroup,
+                             { y: 0,
+                               time: time,
+                               transition: 'linear',
+                               onComplete: function() {
+                                   this.fixed_position_set = false;
+                               }
+                             });
+        }
     },
 
     _onStatusChanged: function(status) {
@@ -77,27 +164,27 @@ const ScreenShield = new Lang.Class({
             if (shouldLock || this._isLocked) {
                 this.lock();
             } else if (this._isModal) {
-                this._popModal();
+                this.unlock();
             }
         }
     },
 
-    _popModal: function() {
-        this._lightbox.hide();
-
-        Main.popModal(this.actor);
-        this.actor.hide();
-
-        this._isModal = false;
-        this._isLocked = false;
-
-        this.emit('lock-status-changed', false);
-    },
-
     _showUnlockDialog: function() {
         if (this._dialog)
             return;
 
+        // Tween the lock screen out of screen
+        // try to use the same speed regardless of original position
+        let h = global.stage.height;
+        let time = CURTAIN_SLIDE_TIME * (h + this._lockScreenGroup.y) / h;
+        Tweener.removeTweens(this._lockScreenGroup);
+        Tweener.addTween(this._lockScreenGroup,
+                         { y: -h,
+                           time: time,
+                           transition: 'linear',
+                           onComplete: Lang.bind(this, this._hideLockScreen),
+                         });
+
         this._dialog = new UnlockDialog.UnlockDialog();
         this._dialog.connect('failed', Lang.bind(this, this._onUnlockFailed));
         this._dialog.connect('unlocked', Lang.bind(this, this._onUnlockSucceded));
@@ -109,46 +196,64 @@ const ScreenShield = new Lang.Class({
             // a security issue
             this._onUnlockSucceded();
         }
-
-        this._dialog._group.raise_top();
     },
 
     _onUnlockFailed: function() {
-        // for now, on failure we just destroy the dialog and create a new
-        // one (this is what gnome-screensaver does)
-        // in the future, we may want to go back to the lock screen instead
-
         this._dialog.destroy();
         this._dialog = null;
 
-        this._showUnlockDialog();
+        this._resetLockScreen();
     },
 
     _onUnlockSucceded: function() {
-        this._dialog.destroy();
-        this._dialog = null;
+        this.unlock();
+    },
 
-        this._popModal();
+    _hideLockScreen: function() {
+        this._arrow.hide();
+        this._lockScreenGroup.hide();
+    },
+
+    _resetLockScreen: function() {
+        this._lockScreenGroup.fixed_position_set = false;
+        this._lockScreenGroup.show();
+        this._arrow.show();
+
+        this._lockScreenGroup.grab_key_focus();
     },
 
     get locked() {
         return this._isLocked;
     },
 
+    unlock: function() {
+        if (this._dialog) {
+            this._dialog.destroy();
+            this._dialog = null;
+        }
+
+        this._lightbox.hide();
+
+        Main.popModal(this.actor);
+        this.actor.hide();
+
+        this._isModal = false;
+        this._isLocked = false;
+
+        this.emit('lock-status-changed', false);
+    },
+
     lock: function() {
         if (!this._isModal) {
             Main.pushModal(this.actor);
             this._isModal = true;
         }
 
-        let wasLocked = this._isLocked;
         this._isLocked = true;
         this.actor.show();
+        this._resetLockScreen();
 
-        this._showUnlockDialog();
-
-        if (!wasLocked)
-            this.emit('lock-status-changed', true);
-    }
+        this.emit('lock-status-changed', true);
+    },
 });
 Signals.addSignalMethods(ScreenShield.prototype);
diff --git a/js/ui/unlockDialog.js b/js/ui/unlockDialog.js
index 4a849c7..8e4a245 100644
--- a/js/ui/unlockDialog.js
+++ b/js/ui/unlockDialog.js
@@ -77,9 +77,11 @@ const UnlockDialog = new Lang.Class({
     Name: 'UnlockDialog',
     Extends: ModalDialog.ModalDialog,
 
-    _init: function() {
+    _init: function(parentActor) {
         this.parent({ shellReactive: true,
-                      styleClass: 'login-dialog' });
+                      styleClass: 'login-dialog',
+                      parentActor: parentActor
+                    });
 
         this._userManager = AccountsService.UserManager.get_default();
         this._userName = GLib.get_user_name();



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