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
This commit is contained in:
Giovanni Campagna 2012-05-24 22:47:48 +02:00
parent 54dc0fd123
commit c0652bcd8f
5 changed files with 161 additions and 42 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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 });

View File

@ -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);

View File

@ -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();