diff --git a/js/Makefile.am b/js/Makefile.am index 3b41635bc..d59b8246d 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -109,6 +109,7 @@ nobase_dist_js_DATA = \ ui/auth/realmd.js \ ui/auth/sessionList.js \ ui/auth/unlockDialog.js \ + ui/auth/userList.js \ ui/auth/util.js \ ui/components/__init__.js \ ui/components/autorunManager.js \ diff --git a/js/ui/auth/loginDialog.js b/js/ui/auth/loginDialog.js index 75a88d25b..2aa582104 100644 --- a/js/ui/auth/loginDialog.js +++ b/js/ui/auth/loginDialog.js @@ -42,6 +42,7 @@ const ModalDialog = imports.ui.modalDialog; const SessionList = imports.ui.auth.sessionList; const Tweener = imports.ui.tweener; const UserAvatar = imports.ui.userAvatar; +const UserList = imports.ui.auth.userList; const UserWidget = imports.ui.userWidget; const _FADE_ANIMATION_TIME = 0.25; @@ -51,242 +52,6 @@ const _LOGO_ICON_HEIGHT = 48; let _loginDialog = null; -const UserListItem = new Lang.Class({ - Name: 'UserListItem', - - _init: function(user) { - this.user = user; - this._userChangedId = this.user.connect('changed', - Lang.bind(this, this._onUserChanged)); - - let layout = new St.BoxLayout({ vertical: false }); - this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', - button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, - can_focus: true, - child: layout, - reactive: true, - x_align: St.Align.START, - x_fill: true }); - - this._userAvatar = new UserAvatar.UserAvatar(this.user, - { styleClass: 'login-dialog-user-list-item-icon' }); - layout.add(this._userAvatar.actor); - let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box', - vertical: true }); - layout.add(textLayout, { expand: true }); - - this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' }); - this.actor.label_actor = this._nameLabel; - textLayout.add(this._nameLabel, - { y_fill: false, - y_align: St.Align.MIDDLE, - expand: true }); - - this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', - scale_x: 0 }); - textLayout.add(this._timedLoginIndicator, - { x_fill: true, - x_align: St.Align.MIDDLE, - y_fill: false, - y_align: St.Align.END }); - - this.actor.connect('clicked', Lang.bind(this, this._onClicked)); - this._onUserChanged(); - }, - - _onUserChanged: function() { - this._nameLabel.set_text(this.user.get_real_name()); - this._userAvatar.update(); - this._updateLoggedIn(); - }, - - syncStyleClasses: function() { - this._updateLoggedIn(); - - if (global.stage.get_key_focus() == this.actor) - this.actor.add_style_pseudo_class('focus'); - else - this.actor.remove_style_pseudo_class('focus'); - }, - - _updateLoggedIn: function() { - if (this.user.is_logged_in()) - this.actor.add_style_pseudo_class('logged-in'); - else - this.actor.remove_style_pseudo_class('logged-in'); - }, - - _onClicked: function() { - this.emit('activate'); - }, - - showTimedLoginIndicator: function(time) { - let hold = new Batch.Hold(); - - this.hideTimedLoginIndicator(); - Tweener.addTween(this._timedLoginIndicator, - { scale_x: 1., - time: time, - transition: 'linear', - onComplete: function() { - hold.release(); - }, - onCompleteScope: this - }); - return hold; - }, - - hideTimedLoginIndicator: function() { - Tweener.removeTweens(this._timedLoginIndicator); - this._timedLoginIndicator.scale_x = 0.; - } -}); -Signals.addSignalMethods(UserListItem.prototype); - -const UserList = new Lang.Class({ - Name: 'UserList', - - _init: function() { - this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view'}); - this.actor.set_policy(Gtk.PolicyType.NEVER, - Gtk.PolicyType.AUTOMATIC); - - this._box = new St.BoxLayout({ vertical: true, - style_class: 'login-dialog-user-list', - pseudo_class: 'expanded' }); - - this.actor.add_actor(this._box); - this._items = {}; - - this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems)); - }, - - _moveFocusToItems: function() { - let hasItems = Object.keys(this._items).length > 0; - - if (!hasItems) - return; - - if (global.stage.get_key_focus() != this.actor) - return; - - let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); - if (!focusSet) { - Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { - this._moveFocusToItems(); - return false; - })); - } - }, - - _onItemActivated: function(activatedItem) { - this.emit('activate', activatedItem); - }, - - updateStyle: function(isExpanded) { - let tasks = []; - - if (isExpanded) - this._box.add_style_pseudo_class('expanded'); - else - this._box.remove_style_pseudo_class('expanded'); - - for (let userName in this._items) { - let item = this._items[userName]; - item.actor.sync_hover(); - item.syncStyleClasses(); - } - }, - - scrollToItem: function(item) { - let box = item.actor.get_allocation_box(); - - let adjustment = this.actor.get_vscroll_bar().get_adjustment(); - - let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); - Tweener.removeTweens(adjustment); - Tweener.addTween (adjustment, - { value: value, - time: _SCROLL_ANIMATION_TIME, - transition: 'easeOutQuad' }); - }, - - jumpToItem: function(item) { - let box = item.actor.get_allocation_box(); - - let adjustment = this.actor.get_vscroll_bar().get_adjustment(); - - let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); - - adjustment.set_value(value); - }, - - getItemFromUserName: function(userName) { - let item = this._items[userName]; - - if (!item) - return null; - - return item; - }, - - addUser: function(user) { - if (!user.is_loaded) - return; - - if (user.is_system_account()) - return; - - if (user.locked) - return; - - let userName = user.get_user_name(); - - if (!userName) - return; - - this.removeUser(user); - - let item = new UserListItem(user); - this._box.add(item.actor, { x_fill: true }); - - this._items[userName] = item; - - item.connect('activate', - Lang.bind(this, this._onItemActivated)); - - // Try to keep the focused item front-and-center - item.actor.connect('key-focus-in', - Lang.bind(this, - function() { - this.scrollToItem(item); - })); - - this._moveFocusToItems(); - - this.emit('item-added', item); - }, - - removeUser: function(user) { - if (!user.is_loaded) - return; - - let userName = user.get_user_name(); - - if (!userName) - return; - - let item = this._items[userName]; - - if (!item) - return; - - item.actor.destroy(); - delete this._items[userName]; - } -}); -Signals.addSignalMethods(UserList.prototype); - const LoginDialog = new Lang.Class({ Name: 'LoginDialog', Extends: ModalDialog.ModalDialog, @@ -350,7 +115,7 @@ const LoginDialog = new Lang.Class({ this._userSelectionBox.add(this._bannerLabel); this._updateBanner(); - this._userList = new UserList(); + this._userList = new UserList.UserList(); this._userSelectionBox.add(this._userList.actor, { expand: true, x_fill: true, diff --git a/js/ui/auth/userList.js b/js/ui/auth/userList.js new file mode 100644 index 000000000..4cc4c4f51 --- /dev/null +++ b/js/ui/auth/userList.js @@ -0,0 +1,251 @@ +// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- + +const AccountsService = imports.gi.AccountsService; +const Gtk = imports.gi.Gtk; +const Meta = imports.gi.Meta; +const Lang = imports.lang; +const Signals = imports.signals; +const St = imports.gi.St; +const Gdm = imports.gi.Gdm; + +const Batch = imports.misc.batch; +const Tweener = imports.ui.tweener; +const UserAvatar = imports.ui.userAvatar; + +const _SCROLL_ANIMATION_TIME = 0.5; + +const UserListItem = new Lang.Class({ + Name: 'UserListItem', + + _init: function(user) { + this.user = user; + this._userChangedId = this.user.connect('changed', + Lang.bind(this, this._onUserChanged)); + + let layout = new St.BoxLayout({ vertical: false }); + this.actor = new St.Button({ style_class: 'login-dialog-user-list-item', + button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, + can_focus: true, + child: layout, + reactive: true, + x_align: St.Align.START, + x_fill: true }); + + this._userAvatar = new UserAvatar.UserAvatar(this.user, + { styleClass: 'login-dialog-user-list-item-icon' }); + layout.add(this._userAvatar.actor); + let textLayout = new St.BoxLayout({ style_class: 'login-dialog-user-list-item-text-box', + vertical: true }); + layout.add(textLayout, { expand: true }); + + this._nameLabel = new St.Label({ style_class: 'login-dialog-user-list-item-name' }); + this.actor.label_actor = this._nameLabel; + textLayout.add(this._nameLabel, + { y_fill: false, + y_align: St.Align.MIDDLE, + expand: true }); + + this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', + scale_x: 0 }); + textLayout.add(this._timedLoginIndicator, + { x_fill: true, + x_align: St.Align.MIDDLE, + y_fill: false, + y_align: St.Align.END }); + + this.actor.connect('clicked', Lang.bind(this, this._onClicked)); + this._onUserChanged(); + }, + + _onUserChanged: function() { + this._nameLabel.set_text(this.user.get_real_name()); + this._userAvatar.update(); + this._updateLoggedIn(); + }, + + syncStyleClasses: function() { + this._updateLoggedIn(); + + if (global.stage.get_key_focus() == this.actor) + this.actor.add_style_pseudo_class('focus'); + else + this.actor.remove_style_pseudo_class('focus'); + }, + + _updateLoggedIn: function() { + if (this.user.is_logged_in()) + this.actor.add_style_pseudo_class('logged-in'); + else + this.actor.remove_style_pseudo_class('logged-in'); + }, + + _onClicked: function() { + this.emit('activate'); + }, + + showTimedLoginIndicator: function(time) { + let hold = new Batch.Hold(); + + this.hideTimedLoginIndicator(); + Tweener.addTween(this._timedLoginIndicator, + { scale_x: 1., + time: time, + transition: 'linear', + onComplete: function() { + hold.release(); + }, + onCompleteScope: this + }); + return hold; + }, + + hideTimedLoginIndicator: function() { + Tweener.removeTweens(this._timedLoginIndicator); + this._timedLoginIndicator.scale_x = 0.; + } +}); +Signals.addSignalMethods(UserListItem.prototype); + +const UserList = new Lang.Class({ + Name: 'UserList', + + _init: function() { + this.actor = new St.ScrollView({ style_class: 'login-dialog-user-list-view'}); + this.actor.set_policy(Gtk.PolicyType.NEVER, + Gtk.PolicyType.AUTOMATIC); + + this._box = new St.BoxLayout({ vertical: true, + style_class: 'login-dialog-user-list', + pseudo_class: 'expanded' }); + + this.actor.add_actor(this._box); + this._items = {}; + + this.actor.connect('key-focus-in', Lang.bind(this, this._moveFocusToItems)); + }, + + _moveFocusToItems: function() { + let hasItems = Object.keys(this._items).length > 0; + + if (!hasItems) + return; + + if (global.stage.get_key_focus() != this.actor) + return; + + let focusSet = this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); + if (!focusSet) { + Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { + this._moveFocusToItems(); + return false; + })); + } + }, + + _onItemActivated: function(activatedItem) { + this.emit('activate', activatedItem); + }, + + updateStyle: function(isExpanded) { + let tasks = []; + + if (isExpanded) + this._box.add_style_pseudo_class('expanded'); + else + this._box.remove_style_pseudo_class('expanded'); + + for (let userName in this._items) { + let item = this._items[userName]; + item.actor.sync_hover(); + item.syncStyleClasses(); + } + }, + + scrollToItem: function(item) { + let box = item.actor.get_allocation_box(); + + let adjustment = this.actor.get_vscroll_bar().get_adjustment(); + + let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); + Tweener.removeTweens(adjustment); + Tweener.addTween (adjustment, + { value: value, + time: _SCROLL_ANIMATION_TIME, + transition: 'easeOutQuad' }); + }, + + jumpToItem: function(item) { + let box = item.actor.get_allocation_box(); + + let adjustment = this.actor.get_vscroll_bar().get_adjustment(); + + let value = (box.y1 + adjustment.step_increment / 2.0) - (adjustment.page_size / 2.0); + + adjustment.set_value(value); + }, + + getItemFromUserName: function(userName) { + let item = this._items[userName]; + + if (!item) + return null; + + return item; + }, + + addUser: function(user) { + if (!user.is_loaded) + return; + + if (user.is_system_account()) + return; + + if (user.locked) + return; + + let userName = user.get_user_name(); + + if (!userName) + return; + + this.removeUser(user); + + let item = new UserListItem(user); + this._box.add(item.actor, { x_fill: true }); + + this._items[userName] = item; + + item.connect('activate', + Lang.bind(this, this._onItemActivated)); + + // Try to keep the focused item front-and-center + item.actor.connect('key-focus-in', + Lang.bind(this, + function() { + this.scrollToItem(item); + })); + + this._moveFocusToItems(); + + this.emit('item-added', item); + }, + + removeUser: function(user) { + if (!user.is_loaded) + return; + + let userName = user.get_user_name(); + + if (!userName) + return; + + let item = this._items[userName]; + + if (!item) + return; + + item.actor.destroy(); + delete this._items[userName]; + } +}); +Signals.addSignalMethods(UserList.prototype);