gnome-shell/js/ui/userWidget.js
Florian Müllner e1c454bd21 userWidget: Stop expand flags from propagating
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>
2024-03-01 23:58:36 +01:00

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