// -*- 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);