[gnome-shell/eos3.8: 31/255] userMenu: Implement the new user menu, based on our design specs



commit 51d93bd6535187cff837e14b4ef8b16ad48d59da
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.
    
     * 2020-03-13:
          + Rebase conflicts, remove deprecated actor property
          + Squash with 6920c53b8
          + Squash with fb9909df8
    
    https://phabricator.endlessm.com/T20327
    https://phabricator.endlessm.com/T20485
    https://phabricator.endlessm.com/T20800

 data/gnome-shell-theme.gresource.xml      |   3 +
 data/theme/endless-help-symbolic.svg      |   1 +
 data/theme/feedback-symbolic.svg          |  12 ++
 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/userMenu.js                         | 187 ++++++++++++++++++++++++++++++
 po/POTFILES.in                            |   1 +
 11 files changed, 284 insertions(+), 17 deletions(-)
---
diff --git a/data/gnome-shell-theme.gresource.xml b/data/gnome-shell-theme.gresource.xml
index b239a8e2a4..1d08658b87 100644
--- a/data/gnome-shell-theme.gresource.xml
+++ b/data/gnome-shell-theme.gresource.xml
@@ -39,8 +39,11 @@
     <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>feedback-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/feedback-symbolic.svg b/data/theme/feedback-symbolic.svg
new file mode 100644
index 0000000000..2df92d0f20
--- /dev/null
+++ b/data/theme/feedback-symbolic.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 17.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd";>
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg"; 
xmlns:xlink="http://www.w3.org/1999/xlink"; x="0px" y="0px"
+        width="12px" height="18px" viewBox="-0.5 0.5 12 18" enable-background="new -0.5 0.5 12 18" 
xml:space="preserve">
+<g id="Captions">
+</g>
+<path 
d="M9.5,11.5h-7l-3-3v3v5c0,1.105,0.895,2,2,2h8c1.105,0,2-0.895,2-2v-3C11.5,12.395,10.605,11.5,9.5,11.5z 
M9.5,16.5h-8v-1h8
+       V16.5z M9.5,14.5h-8v-1h8V14.5z"/>
+<path d="M9.5,0.5h-8c-1.105,0-2,0.895-2,2v3c0,1.105,0.895,2,2,2h7l3,3v-3v-5C11.5,1.395,10.605,0.5,9.5,0.5z 
M9.5,5.5h-8v-1h8V5.5z
+        M9.5,3.5h-8v-1h8V3.5z"/>
+</svg>
diff --git a/data/theme/gnome-shell-sass/_endless.scss b/data/theme/gnome-shell-sass/_endless.scss
index fe0fd940ee..c7bf1aabba 100644
--- a/data/theme/gnome-shell-sass/_endless.scss
+++ b/data/theme/gnome-shell-sass/_endless.scss
@@ -95,6 +95,11 @@
     -arrow-background-color: #000000;
 }
 
+popup-separator-menu-item {
+  margin: 12px 0;
+  padding: 0;
+}
+
 // Apps Icon Bar
 
 #appIconBar {
@@ -215,6 +220,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 e2ef9f48de..1baea14d28 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -147,6 +147,7 @@
     <file>ui/forceAppExitDialog.js</file>
     <file>ui/hotCorner.js</file>
     <file>ui/sideComponent.js</file>
+    <file>ui/userMenu.js</file>
     <file>ui/workspaceMonitor.js</file>
   </gresource>
 </gresources>
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 218506d960..d387d123a6 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
@@ -768,17 +765,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);
 
@@ -800,6 +786,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() {
@@ -838,6 +856,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 5bb4680eea..20ab2db52e 100644
--- a/js/ui/popupMenu.js
+++ b/js/ui/popupMenu.js
@@ -541,7 +541,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);
 
@@ -552,7 +552,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 4cc4be895f..d24ab760cb 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -89,7 +89,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/userMenu.js b/js/ui/userMenu.js
new file mode 100644
index 0000000000..a3d35ebbdd
--- /dev/null
+++ b/js/ui/userMenu.js
@@ -0,0 +1,187 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+/* exported UserMenu */
+
+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 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 FEEDBACK_TEXT = _('Give Us Feedback');
+const FEEDBACK_LAUNCHER = "eos-link-feedback.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.userIconItem.set({
+            x_align: Clutter.ActorAlign.CENTER,
+            x_expand: true,
+        });
+
+        this._user = user;
+        this._avatar = new UserWidget.Avatar(this._user, {
+            reactive: true,
+            styleClass: 'user-menu-avatar',
+        });
+        this._avatar.x_align = Clutter.ActorAlign.CENTER;
+
+        let iconButton = new St.Button({ child: this._avatar });
+        this.userIconItem.add_child(iconButton);
+
+        iconButton.connect('clicked', () => {
+            if (Main.sessionMode.allowSettings)
+                this.userIconItem.activate(null);
+        });
+
+        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({
+            x_align: Clutter.ActorAlign.CENTER,
+            x_expand: true,
+            ellipsize: Pango.EllipsizeMode.NONE,
+            line_wrap: true,
+        });
+        this.userLabelItem.add_child(this._label);
+        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);
+    }
+
+    _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._launchApplication(SETTINGS_LAUNCHER);
+        }, gicon);
+
+        // Social
+        gicon = new Gio.ThemedIcon({ name: 'user-available-symbolic' });
+        menuItemsSection.addSettingsAction(ONLINE_ACCOUNTS_TEXT, ONLINE_ACCOUNTS_PANEL_LAUNCHER, gicon);
+
+        // Feedback
+        let iconFile = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/feedback-symbolic.svg');
+        gicon = new Gio.FileIcon({ file: iconFile });
+        menuItemsSection.addAction(FEEDBACK_TEXT, () => {
+            this._launchApplication(FEEDBACK_LAUNCHER);
+        }, gicon);
+        this.menu.addMenuItem(menuItemsSection);
+
+        // Help center
+        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 bae933a610..2d39b74683 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -101,3 +101,4 @@ js/ui/appIconBar.js
 js/ui/endlessButton.js
 js/ui/forceAppExitDialog.js
 js/ui/hotCorner.js
+js/ui/userMenu.js


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]