e1c454bd21
Since commit 9c2da01a9, the avatar's child is expanded to account for the new St.Bin behavior. However as expand flags are propagated up, this now results in avatar actor getting unintentionally expanded in places like the end-session dialog. Stop this by explicitly setting the avatar actor to not expand. Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3221>
220 lines
6.5 KiB
JavaScript
220 lines
6.5 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
//
|
|
// A widget showing the user avatar and name
|
|
|
|
import Clutter from 'gi://Clutter';
|
|
import GLib from 'gi://GLib';
|
|
import GObject from 'gi://GObject';
|
|
import St from 'gi://St';
|
|
|
|
import * as Params from '../misc/params.js';
|
|
|
|
const AVATAR_ICON_SIZE = 64;
|
|
|
|
// Adapted from gdm/gui/user-switch-applet/applet.c
|
|
//
|
|
// Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
|
|
// Copyright (C) 2008,2009 Red Hat, Inc.
|
|
|
|
export const Avatar = GObject.registerClass(
|
|
class Avatar extends St.Bin {
|
|
_init(user, params) {
|
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
params = Params.parse(params, {
|
|
styleClass: 'user-icon',
|
|
reactive: false,
|
|
iconSize: AVATAR_ICON_SIZE,
|
|
});
|
|
|
|
super._init({
|
|
style_class: params.styleClass,
|
|
reactive: params.reactive,
|
|
width: params.iconSize * themeContext.scaleFactor,
|
|
height: params.iconSize * themeContext.scaleFactor,
|
|
x_expand: false,
|
|
y_expand: false,
|
|
});
|
|
|
|
this._iconSize = params.iconSize;
|
|
this._user = user;
|
|
|
|
this.bind_property('reactive', this, 'track-hover',
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
this.bind_property('reactive', this, 'can-focus',
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
|
|
// Monitor the scaling factor to make sure we recreate the avatar when needed.
|
|
themeContext.connectObject('notify::scale-factor', this.update.bind(this), this);
|
|
}
|
|
|
|
vfunc_style_changed() {
|
|
super.vfunc_style_changed();
|
|
|
|
let node = this.get_theme_node();
|
|
let [found, iconSize] = node.lookup_length('icon-size', false);
|
|
|
|
if (!found)
|
|
return;
|
|
|
|
let themeContext = St.ThemeContext.get_for_stage(global.stage);
|
|
|
|
// node.lookup_length() returns a scaled value, but we
|
|
// need unscaled
|
|
this._iconSize = iconSize / themeContext.scaleFactor;
|
|
this.update();
|
|
}
|
|
|
|
setSensitive(sensitive) {
|
|
this.reactive = sensitive;
|
|
}
|
|
|
|
update() {
|
|
let iconFile = null;
|
|
if (this._user) {
|
|
iconFile = this._user.get_icon_file();
|
|
if (iconFile && !GLib.file_test(iconFile, GLib.FileTest.EXISTS))
|
|
iconFile = null;
|
|
}
|
|
|
|
let {scaleFactor} = St.ThemeContext.get_for_stage(global.stage);
|
|
this.set_size(
|
|
this._iconSize * scaleFactor,
|
|
this._iconSize * scaleFactor);
|
|
|
|
if (iconFile) {
|
|
this.child = null;
|
|
this.add_style_class_name('user-avatar');
|
|
this.style = `
|
|
background-image: url("${iconFile}");
|
|
background-size: cover;`;
|
|
} else {
|
|
this.style = null;
|
|
this.child = new St.Icon({
|
|
icon_name: 'avatar-default-symbolic',
|
|
icon_size: this._iconSize,
|
|
x_expand: true,
|
|
y_expand: true,
|
|
});
|
|
}
|
|
}
|
|
});
|
|
|
|
export const UserWidgetLabel = GObject.registerClass(
|
|
class UserWidgetLabel extends St.Widget {
|
|
_init(user) {
|
|
super._init({layout_manager: new Clutter.BinLayout()});
|
|
|
|
this._user = user;
|
|
|
|
this._realNameLabel = new St.Label({
|
|
style_class: 'user-widget-label',
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this.add_child(this._realNameLabel);
|
|
|
|
this._userNameLabel = new St.Label({
|
|
style_class: 'user-widget-label',
|
|
y_align: Clutter.ActorAlign.CENTER,
|
|
});
|
|
this.add_child(this._userNameLabel);
|
|
|
|
this._currentLabel = null;
|
|
|
|
this._user.connectObject(
|
|
'notify::is-loaded', this._updateUser.bind(this),
|
|
'changed', this._updateUser.bind(this), this);
|
|
this._updateUser();
|
|
}
|
|
|
|
vfunc_allocate(box) {
|
|
this.set_allocation(box);
|
|
|
|
let availWidth = box.x2 - box.x1;
|
|
let availHeight = box.y2 - box.y1;
|
|
|
|
let [, , natRealNameWidth] = this._realNameLabel.get_preferred_size();
|
|
|
|
let childBox = new Clutter.ActorBox();
|
|
|
|
let hiddenLabel;
|
|
if (natRealNameWidth <= availWidth) {
|
|
this._currentLabel = this._realNameLabel;
|
|
hiddenLabel = this._userNameLabel;
|
|
} else {
|
|
this._currentLabel = this._userNameLabel;
|
|
hiddenLabel = this._realNameLabel;
|
|
}
|
|
this.label_actor = this._currentLabel;
|
|
|
|
hiddenLabel.allocate(childBox);
|
|
|
|
childBox.set_size(availWidth, availHeight);
|
|
|
|
this._currentLabel.allocate(childBox);
|
|
}
|
|
|
|
vfunc_paint(paintContext) {
|
|
this._currentLabel.paint(paintContext);
|
|
}
|
|
|
|
_updateUser() {
|
|
if (this._user.is_loaded) {
|
|
this._realNameLabel.text = this._user.get_real_name();
|
|
this._userNameLabel.text = this._user.get_user_name();
|
|
} else {
|
|
this._realNameLabel.text = '';
|
|
this._userNameLabel.text = '';
|
|
}
|
|
}
|
|
});
|
|
|
|
export const UserWidget = GObject.registerClass(
|
|
class UserWidget extends St.BoxLayout {
|
|
_init(user, orientation = Clutter.Orientation.HORIZONTAL) {
|
|
// If user is null, that implies a username-based login authorization.
|
|
this._user = user;
|
|
|
|
let vertical = orientation === Clutter.Orientation.VERTICAL;
|
|
let xAlign = vertical ? Clutter.ActorAlign.CENTER : Clutter.ActorAlign.START;
|
|
let styleClass = vertical ? 'user-widget vertical' : 'user-widget horizontal';
|
|
|
|
super._init({
|
|
styleClass,
|
|
vertical,
|
|
xAlign,
|
|
});
|
|
|
|
this._avatar = new Avatar(user);
|
|
this._avatar.x_align = Clutter.ActorAlign.CENTER;
|
|
this.add_child(this._avatar);
|
|
|
|
this._userLoadedId = 0;
|
|
this._userChangedId = 0;
|
|
if (user) {
|
|
this._label = new UserWidgetLabel(user);
|
|
this.add_child(this._label);
|
|
|
|
this._label.bind_property('label-actor',
|
|
this, 'label-actor',
|
|
GObject.BindingFlags.SYNC_CREATE);
|
|
|
|
this._user.connectObject(
|
|
'notify::is-loaded', this._updateUser.bind(this),
|
|
'changed', this._updateUser.bind(this), this);
|
|
} else {
|
|
this._label = new St.Label({
|
|
style_class: 'user-widget-label',
|
|
text: 'Empty User',
|
|
opacity: 0,
|
|
});
|
|
this.add_child(this._label);
|
|
}
|
|
|
|
this._updateUser();
|
|
}
|
|
|
|
_updateUser() {
|
|
this._avatar.update();
|
|
}
|
|
});
|