[gnome-shell/T27795: 41/138] userMenu: Implement the new user menu, based on our design specs
- From: Georges Basile Stavracas Neto <gbsneto src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell/T27795: 41/138] userMenu: Implement the new user menu, based on our design specs
- Date: Tue, 1 Oct 2019 23:32:46 +0000 (UTC)
commit 79da0ab60319299e8123620a71a6792e61ccdcc7
Author: Mario Sanchez Prada <mario endlessm com>
Date: Wed Feb 14 11:15:02 2018 +0000
userMenu: Implement the new user menu, based on our design specs
This is a fresh implementation from scratch based on the Endless
design specs from T20327, T20485 and T20800, which includes all
the additions incorporated since EOS 3.2 squashed together.
https://phabricator.endlessm.com/T20327
https://phabricator.endlessm.com/T20485
https://phabricator.endlessm.com/T20800
data/gnome-shell-theme.gresource.xml | 2 +
data/theme/endless-help-symbolic.svg | 1 +
data/theme/gnome-shell-sass/_endless.scss | 42 ++++++++
data/theme/system-logout.png | Bin 0 -> 18189 bytes
js/js-resources.gresource.xml | 1 +
js/ui/panel.js | 47 ++++++---
js/ui/popupMenu.js | 4 +-
js/ui/sessionMode.js | 3 +-
js/ui/status/system.js | 66 ++++++------
js/ui/userMenu.js | 170 ++++++++++++++++++++++++++++++
po/POTFILES.in | 1 +
11 files changed, 286 insertions(+), 51 deletions(-)
---
diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml
index c90a81a467..583e700788 100644
--- a/data/gnome-shell-theme.gresource.xml
+++ b/data/gnome-shell-theme.gresource.xml
@@ -39,8 +39,10 @@
<file>corner-ripple-tl.png</file>
<file>corner-ripple-tr.png</file>
<file>endless-button-symbolic.svg</file>
+ <file>endless-help-symbolic.svg</file>
<file>hot-corner-symbolic.svg</file>
<file>hot-corner-rtl-symbolic.svg</file>
<file>mini-icon-active-indicator.png</file>
+ <file>system-logout.png</file>
</gresource>
</gresources>
diff --git a/data/theme/endless-help-symbolic.svg b/data/theme/endless-help-symbolic.svg
new file mode 100755
index 0000000000..cf88058f39
--- /dev/null
+++ b/data/theme/endless-help-symbolic.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>Asset 4</title><rect width="16"
height="16" style="opacity:0.020000020042061806;isolation:isolate"/><path
d="M12,1.529H4a2.889,2.889,0,0,0-3,2.88v5.24a2.889,2.889,0,0,0,3,2.88H5.475l2.463,2.463,2.587-2.463H12a2.889,2.889,0,0,0,3-2.88V4.409A2.889,2.889,0,0,0,12,1.529ZM7.949,3.417c1.619,0,2.429.97,2.429,1.969a2.138,2.138,0,0,1-.97,1.8l-.38.279a1.154,1.154,0,0,0-.589.97H7.249A2.069,2.069,0,0,1,7.209,8a1.721,1.721,0,0,1,.76-1.409l.51-.38a.82.82,0,0,0,.359-.7.792.792,0,0,0-.889-.77.914.914,0,0,0-.919.939c0,.069,0,.126,0,.173L5.629,5.782c-.006-.071-.009-.141-.009-.206A2.159,2.159,0,0,1,7.949,3.417Zm.8,6.477a.865.865,0,1,1-.87-.869A.868.868,0,0,1,8.748,9.894Z"
style="fill:#bebebe"/></svg>
\ No newline at end of file
diff --git a/data/theme/gnome-shell-sass/_endless.scss b/data/theme/gnome-shell-sass/_endless.scss
index 64a7ff1571..38f5b969ef 100644
--- a/data/theme/gnome-shell-sass/_endless.scss
+++ b/data/theme/gnome-shell-sass/_endless.scss
@@ -92,6 +92,11 @@
-arrow-background-color: #000000;
}
+popup-separator-menu-item {
+ margin: 12px 0;
+ padding: 0;
+}
+
// Apps Icon Bar
#appIconBar {
@@ -212,6 +217,43 @@
}
}
+// User Menu
+
+.user-menu-button-icon {
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ border-radius: 17px;
+ background-size: 34px;
+ padding: 0px;
+
+ &:hover {
+ border-radius: 18px;
+ background-size: 36px;
+ }
+}
+
+.user-menu-avatar {
+ width: 60px;
+ height: 60px;
+ background-size: contain;
+ background-color: rgba(50, 50, 50, 0.80);
+ border-radius: 30px;
+ border: 1px solid #000;
+
+ &:hover {
+ border: 1px solid #555;
+ }
+}
+
+.user-menu-name {
+ text-align: center;
+ font-weight: bold;
+ color: white;
+}
+
+.user-menu-items {
+ margin: 6px 0px;
+}
+
// Endless Button
%endless-button-hover {
diff --git a/data/theme/system-logout.png b/data/theme/system-logout.png
new file mode 100644
index 0000000000..e6de3e64ed
Binary files /dev/null and b/data/theme/system-logout.png differ
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index 215de58dcc..48cb070d01 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -149,5 +149,6 @@
<file>ui/monitor.js</file>
<file>ui/sideComponent.js</file>
<file>ui/status/orientation.js</file>
+ <file>ui/userMenu.js</file>
</gresource>
</gresources>
diff --git a/js/ui/panel.js b/js/ui/panel.js
index de65a134a9..aebb06056c 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -21,9 +21,6 @@ var APP_MENU_ICON_MARGIN = 0;
var BUTTON_DND_ACTIVATION_TIMEOUT = 250;
-const SETTINGS_TEXT = _("All Settingsā¦");
-const CONTROL_CENTER_LAUNCHER = "gnome-control-center.desktop";
-
// To make sure the panel corners blend nicely with the panel,
// we draw background and borders the same way, e.g. drawing
// them as filled shapes from the outside inwards instead of
@@ -759,17 +756,6 @@ class AggregateMenu extends PanelMenu.Button {
if (!userMode)
this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
- let gicon = new Gio.ThemedIcon({ name: 'applications-system-symbolic' });
- this.menu.addAction(SETTINGS_TEXT, () => {
- this.menu.close(BoxPointer.PopupAnimation.NONE);
- Main.overview.hide();
-
- let app = Shell.AppSystem.get_default().lookup_app(CONTROL_CENTER_LAUNCHER);
- let context = new AppActivation.AppActivationContext(app);
- context.activate();
- }, gicon);
-
- this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
if (this._network) {
this.menu.addMenuItem(this._network.menu);
}
@@ -795,6 +781,38 @@ class AggregateMenu extends PanelMenu.Button {
}
});
+var UserMenu = GObject.registerClass(
+class UserMenu extends PanelMenu.Button {
+ _init() {
+ super._init(0.0, C_("User menu", "User Menu"), false);
+
+ this.accessible_role = Atk.Role.MENU;
+
+ let menuLayout = new AggregateLayout();
+ this.menu.box.set_layout_manager(menuLayout);
+ this.menu.actor.add_style_class_name('aggregate-menu');
+
+ this._userMenu = new imports.ui.userMenu.UserMenu();
+ this.add_child(this._userMenu.panelBox);
+ this.menu.addMenuItem(this._userMenu.menu);
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ let systemIndicator = new imports.ui.status.system.Indicator();
+ this.menu.addMenuItem(systemIndicator.menu);
+
+ menuLayout.addSizeChild(systemIndicator.menu.actor);
+
+ // We need to monitor the session to know when to show the button
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ _sessionUpdated() {
+ this.visible = !Main.sessionMode.isGreeter;
+ this.setSensitive(!Main.sessionMode.isLocked);
+ }
+});
+
var PowerMenu = GObject.registerClass(
class PopoverMenu extends PanelMenu.SingleIconButton {
_init() {
@@ -833,6 +851,7 @@ const PANEL_ITEM_IMPLEMENTATIONS = {
'dwellClick': imports.ui.status.dwellClick.DwellClickIndicator,
'hotCorner': imports.ui.hotCorner.HotCorner,
'powerMenu': PowerMenu,
+ 'userMenu': UserMenu,
};
var Panel = GObject.registerClass(
diff --git a/js/ui/popupMenu.js b/js/ui/popupMenu.js
index d5d5fdd265..052b879186 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -502,7 +502,7 @@ var PopupMenuBase = class {
return menuItem;
}
- addSettingsAction(title, desktopFile) {
+ addSettingsAction(title, desktopFile, icon) {
let menuItem = this.addAction(title, () => {
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
@@ -513,7 +513,7 @@ var PopupMenuBase = class {
Main.overview.hide();
app.activate();
- });
+ }, icon);
menuItem.visible = Main.sessionMode.allowSettings;
this._settingsActions[desktopFile] = menuItem;
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
index 2212a16b4c..0352712649 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -103,7 +103,8 @@ const _modes = {
panel: {
left: ['endlessButton', 'appIcons'],
center: [],
- right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu', 'dateMenu', 'hotCorner']
+ right: ['dwellClick', 'a11y', 'keyboard', 'aggregateMenu', 'dateMenu',
+ 'userMenu', 'hotCorner']
}
}
};
diff --git a/js/ui/status/system.js b/js/ui/status/system.js
index 41ddac13b3..467f5278dc 100644
--- a/js/ui/status/system.js
+++ b/js/ui/status/system.js
@@ -1,7 +1,8 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
-const { AccountsService, Clutter, GLib, GObject, Shell, St } = imports.gi;
+const { AccountsService, Clutter, Gio,
+ GLib, GObject, Shell, St } = imports.gi;
const BoxPointer = imports.ui.boxpointer;
const SystemActions = imports.misc.systemActions;
@@ -132,9 +133,6 @@ var Indicator = class extends PanelMenu.SystemIndicator {
this._systemActions.forceUpdate();
});
this._updateMultiUser();
-
- Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
- this._sessionUpdated();
}
_sessionUpdated() {
@@ -163,14 +161,30 @@ var Indicator = class extends PanelMenu.SystemIndicator {
this._switchUserSubMenu.label.text = this._user.get_user_name();
}
+ _createActionButtonBase(accessibleName) {
+ let button = new St.Button({
+ reactive: true,
+ can_focus: true,
+ track_hover: true,
+ accessible_name: accessibleName,
+ style_class: 'system-menu-action',
+ });
+ return button;
+ }
+
_createActionButton(iconName, accessibleName) {
- let icon = new St.Button({ reactive: true,
- can_focus: true,
- track_hover: true,
- accessible_name: accessibleName,
- style_class: 'system-menu-action' });
- icon.child = new St.Icon({ icon_name: iconName });
- return icon;
+ let button = this._createActionButtonBase(accessibleName);
+ button.child = new St.Icon({ icon_name: iconName });
+ return button;
+ }
+
+ _createActionButtonForIconPath(iconPath, accessibleName) {
+ let iconFile = Gio.File.new_for_uri('resource:///org/gnome/shell' + iconPath);
+ let gicon = new Gio.FileIcon({ file: iconFile });
+
+ let button = this._createActionButtonBase(accessibleName);
+ button.child = new St.Icon({ gicon: gicon });
+ return button;
}
_createSubMenu() {
@@ -219,28 +233,18 @@ var Indicator = class extends PanelMenu.SystemIndicator {
this._user.connect('notify::is-loaded', this._updateSwitchUserSubMenu.bind(this));
this._user.connect('changed', this._updateSwitchUserSubMenu.bind(this));
- this.menu.addMenuItem(this._switchUserSubMenu);
-
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
item = new PopupMenu.PopupBaseMenuItem({ reactive: false,
can_focus: false });
this.buttonGroup = item;
- let app = this._settingsApp = Shell.AppSystem.get_default().lookup_app(
- 'gnome-control-center.desktop'
- );
- if (app) {
- let [icon, name] = [app.app_info.get_icon().names[0],
- app.get_name()];
- this._settingsAction = this._createActionButton(icon, name);
- this._settingsAction.connect('clicked',
- this._onSettingsClicked.bind(this));
- } else {
- log('Missing required core component Settings, expect troubleā¦');
- this._settingsAction = new St.Widget();
- }
- item.add(this._settingsAction, { expand: true, x_fill: false });
+ this._logoutAction = this._createActionButtonForIconPath('/theme/system-logout.png', _("Log Out"));
+ this._logoutAction.connect('clicked', () => {
+ this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
+ this._systemActions.activateLogout();
+ });
+ item.actor.add(this._logoutAction, { expand: true, x_fill: false });
this._lockScreenAction = this._createActionButton('changes-prevent', _("Lock"));
this._lockScreenAction.connect('clicked', () => {
@@ -279,7 +283,7 @@ var Indicator = class extends PanelMenu.SystemIndicator {
this.menu.addMenuItem(item);
let visibilityGroup = [
- this._settingsAction,
+ this._logoutAction,
this._lockScreenAction,
this._altSwitcher.actor,
];
@@ -290,10 +294,4 @@ var Indicator = class extends PanelMenu.SystemIndicator {
});
}
}
-
- _onSettingsClicked() {
- this.menu.itemActivated();
- Main.overview.hide();
- this._settingsApp.activate();
- }
};
diff --git a/js/ui/userMenu.js b/js/ui/userMenu.js
new file mode 100644
index 0000000000..056fa47384
--- /dev/null
+++ b/js/ui/userMenu.js
@@ -0,0 +1,170 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const { AccountsService, Clutter, GLib,
+ Gio, Pango, Shell, St } = imports.gi;
+
+const AppActivation = imports.ui.appActivation;
+const BoxPointer = imports.ui.boxpointer;
+const Main = imports.ui.main;
+const Panel = imports.ui.panel;
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+const UserWidget = imports.ui.userWidget;
+
+const USER_ICON_SIZE = 34;
+
+const ONLINE_ACCOUNTS_TEXT = _("Social Accounts");
+const ONLINE_ACCOUNTS_PANEL_LAUNCHER = 'gnome-online-accounts-panel.desktop';
+
+const SETTINGS_TEXT = _("Settings");
+const SETTINGS_LAUNCHER = "gnome-control-center.desktop";
+
+const USER_ACCOUNTS_PANEL_LAUNCHER = 'gnome-user-accounts-panel.desktop';
+
+const HELP_CENTER_TEXT = _("Help");
+const HELP_CENTER_LAUNCHER = 'org.gnome.Yelp.desktop';
+
+const UserAccountSection = class extends PopupMenu.PopupMenuSection {
+ constructor(user) {
+ super();
+
+ // User account's icon
+ this.userIconItem = new PopupMenu.PopupBaseMenuItem({
+ reactive: false,
+ can_focus: false,
+ });
+ this._user = user;
+ this._avatar = new UserWidget.Avatar(this._user, {
+ reactive: true,
+ styleClass: 'user-menu-avatar',
+ });
+ let iconButton = new St.Button({ child: this._avatar.actor });
+ this.userIconItem.add(iconButton, { expand: true, span: -1 });
+
+ iconButton.connect('clicked', () => {
+ if (Main.sessionMode.allowSettings)
+ this.userIconItem.activate();
+ });
+
+ this.userIconItem.connect('notify::sensitive', () => {
+ this._avatar.setSensitive(this.userIconItem.getSensitive);
+ });
+ this.addMenuItem(this.userIconItem);
+
+ // User account's name
+ this.userLabelItem = new PopupMenu.PopupBaseMenuItem({
+ reactive: false,
+ can_focus: false,
+ });
+ this._label = new St.Label({ style_class: 'user-menu-name' });
+ this._label.clutter_text.set_line_wrap(true);
+ this._label.clutter_text.set_ellipsize(Pango.EllipsizeMode.NONE);
+ this.userLabelItem.add(this._label, { expand: true, span: -1 });
+ this.addMenuItem(this.userLabelItem);
+
+ // We need to monitor the session to know when to enable the user avatar
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ _sessionUpdated() {
+ this.userIconItem.setSensitive(Main.sessionMode.allowSettings);
+ }
+
+ update() {
+ this._avatar.update();
+
+ if (this._user.is_loaded)
+ this._label.set_text(this._user.get_real_name());
+ else
+ this._label.set_text('');
+ }
+};
+
+var UserMenu = class {
+ constructor() {
+ this._userManager = AccountsService.UserManager.get_default();
+ this._user = this._userManager.get_user(GLib.get_user_name());
+
+ this._user.connect('notify::is-loaded', this._updateUser.bind(this));
+ this._user.connect('changed', this._updateUser.bind(this));
+
+ this._createPanelIcon();
+ this._createPopupMenu();
+
+ this._updateUser();
+ }
+
+ _createPanelIcon() {
+ this.panelBox = new St.BoxLayout({
+ x_align: Clutter.ActorAlign.CENTER,
+ y_align: Clutter.ActorAlign.CENTER,
+ });
+ this._panelAvatar = new UserWidget.Avatar(this._user, {
+ iconSize: USER_ICON_SIZE,
+ styleClass: 'user-menu-button-icon',
+ reactive: true,
+ });
+ this.panelBox.add_actor(this._panelAvatar.actor);
+ }
+
+ _createPopupMenu() {
+ this.menu = new PopupMenu.PopupMenuSection();
+
+ this._accountSection = new UserAccountSection(this._user);
+ this._accountSection.userIconItem.connect('activate', () => {
+ this._launchApplication(USER_ACCOUNTS_PANEL_LAUNCHER);
+ });
+
+ this.menu.addMenuItem(this._accountSection);
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ let menuItemsSection = new PopupMenu.PopupMenuSection();
+ menuItemsSection.box.style_class = 'user-menu-items';
+
+ // Control Center
+ let gicon = new Gio.ThemedIcon({ name: 'applications-system-symbolic' });
+ this._settingsItem = menuItemsSection.addAction(SETTINGS_TEXT, () => {
+ this.menu.close(BoxPointer.PopupAnimation.NONE);
+ Main.overview.hide();
+
+ let app = Shell.AppSystem.get_default().lookup_app(SETTINGS_LAUNCHER);
+ let context = new AppActivation.AppActivationContext(app);
+ context.activate();
+ }, gicon);
+
+ // Social
+ gicon = new Gio.ThemedIcon({ name: 'user-available-symbolic' });
+ menuItemsSection.addSettingsAction(ONLINE_ACCOUNTS_TEXT, ONLINE_ACCOUNTS_PANEL_LAUNCHER, gicon);
+
+ // Help center
+ let iconFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/endless-help-symbolic.svg')
+ gicon = new Gio.FileIcon({ file: iconFile });
+ menuItemsSection.addAction(HELP_CENTER_TEXT, () => {
+ this._launchApplication(HELP_CENTER_LAUNCHER);
+ }, gicon);
+ this.menu.addMenuItem(menuItemsSection);
+
+ // We need to monitor the session to know when to show/hide the settings item
+ Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
+ this._sessionUpdated();
+ }
+
+ _launchApplication(launcherName) {
+ this.menu.close(BoxPointer.PopupAnimation.NONE);
+ Main.overview.hide();
+
+ let app = Shell.AppSystem.get_default().lookup_app(launcherName);
+ let context = new AppActivation.AppActivationContext(app);
+ context.activate();
+ }
+
+ _updateUser() {
+ this._panelAvatar.update();
+ this._accountSection.update();
+ }
+
+ _sessionUpdated() {
+ this._settingsItem.visible = Main.sessionMode.allowSettings;
+ }
+};
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 77aea9b8b2..955765760d 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -98,3 +98,4 @@ js/ui/endlessButton.js
js/ui/forceAppExitDialog.js
js/ui/hotCorner.js
js/ui/status/orientation.js
+js/ui/userMenu.js
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]