system: Replace action icons with regular menu items

Besides making the menu a bit less special, it allows us to fit both
shutdown and suspend actions without any hidden alt-key Easter eggs.

https://gitlab.gnome.org/GNOME/gnome-shell/issues/270
This commit is contained in:
Florian Müllner 2019-10-15 20:51:33 +02:00 committed by Florian Müllner
parent e4147f3611
commit 147a743d8d
3 changed files with 72 additions and 259 deletions

View File

@ -1190,7 +1190,8 @@ StScrollBar {
.aggregate-menu { .aggregate-menu {
min-width: 21em; min-width: 21em;
.popup-menu-icon { padding: 0 4px; } .popup-menu-icon { padding: 0 4px;
-st-icon-style: symbolic; }
.popup-sub-menu .popup-menu-item > :first-child { .popup-sub-menu .popup-menu-item > :first-child {
&:ltr { /* 12px spacing + 2*4px padding */ &:ltr { /* 12px spacing + 2*4px padding */
padding-left: 20px; margin-left: 1.09em; } padding-left: 20px; margin-left: 1.09em; }
@ -1199,27 +1200,6 @@ StScrollBar {
} }
} }
.system-menu-action {
-st-icon-style: symbolic;
color: $fg_color;
border-radius: 32px; /* wish we could do 50% */
padding: 13px;
border: 1px solid $_bubble_borders_color;
&:hover, &:focus {
background-color: $_hover_bg_color;
color: $fg_color;
border: none;
padding: 14px;
}
&:active {
background-color: $selected_bg_color;
color: $selected_fg_color;
}
& > StIcon { icon-size: 16px; }
}
// Activities Ripples // Activities Ripples
.ripple-box { .ripple-box {
width: 52px; width: 52px;

View File

@ -760,12 +760,13 @@ class AggregateMenu extends PanelMenu.Button {
this.menu.addMenuItem(this._rfkill.menu); this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu); this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(this._nightLight.menu); this.menu.addMenuItem(this._nightLight.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._system.menu); this.menu.addMenuItem(this._system.menu);
menuLayout.addSizeChild(this._location.menu.actor); menuLayout.addSizeChild(this._location.menu.actor);
menuLayout.addSizeChild(this._rfkill.menu.actor); menuLayout.addSizeChild(this._rfkill.menu.actor);
menuLayout.addSizeChild(this._power.menu.actor); menuLayout.addSizeChild(this._power.menu.actor);
menuLayout.addSizeChild(this._system.buttonGroup); menuLayout.addSizeChild(this._system.menu.actor);
} }
}); });

View File

@ -1,7 +1,7 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */ /* exported Indicator */
const { AccountsService, Clutter, GLib, GObject, Shell, St } = imports.gi; const { GObject, Shell, St } = imports.gi;
const BoxPointer = imports.ui.boxpointer; const BoxPointer = imports.ui.boxpointer;
const SystemActions = imports.misc.systemActions; const SystemActions = imports.misc.systemActions;
@ -10,120 +10,11 @@ const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
var AltSwitcher = GObject.registerClass(
class AltSwitcher extends St.Bin {
_init(standard, alternate) {
super._init();
this._standard = standard;
this._standard.connect('notify::visible', this._sync.bind(this));
if (this._standard instanceof St.Button)
this._standard.connect('clicked',
() => this._clickAction.release());
this._alternate = alternate;
this._alternate.connect('notify::visible', this._sync.bind(this));
if (this._alternate instanceof St.Button)
this._alternate.connect('clicked',
() => this._clickAction.release());
this._capturedEventId = global.stage.connect('captured-event', this._onCapturedEvent.bind(this));
this._flipped = false;
this._clickAction = new Clutter.ClickAction();
this._clickAction.connect('long-press', this._onLongPress.bind(this));
this.connect('destroy', this._onDestroy.bind(this));
}
vfunc_map() {
super.vfunc_map();
this._flipped = false;
}
vfunc_unmap() {
super.vfunc_unmap();
this._flipped = false;
}
_sync() {
let childToShow = null;
if (this._standard.visible && this._alternate.visible) {
let [x_, y_, mods] = global.get_pointer();
let altPressed = (mods & Clutter.ModifierType.MOD1_MASK) != 0;
if (this._flipped)
childToShow = altPressed ? this._standard : this._alternate;
else
childToShow = altPressed ? this._alternate : this._standard;
} else if (this._standard.visible) {
childToShow = this._standard;
} else if (this._alternate.visible) {
childToShow = this._alternate;
} else {
this.hide();
return;
}
let childShown = this.get_child();
if (childShown != childToShow) {
if (childShown) {
if (childShown.fake_release)
childShown.fake_release();
childShown.remove_action(this._clickAction);
}
childToShow.add_action(this._clickAction);
let hasFocus = this.contains(global.stage.get_key_focus());
this.set_child(childToShow);
if (hasFocus)
childToShow.grab_key_focus();
// The actors might respond to hover, so
// sync the pointer to make sure they update.
global.sync_pointer();
}
this.show();
}
_onDestroy() {
if (this._capturedEventId > 0) {
global.stage.disconnect(this._capturedEventId);
this._capturedEventId = 0;
}
}
_onCapturedEvent(actor, event) {
let type = event.type();
if (type == Clutter.EventType.KEY_PRESS || type == Clutter.EventType.KEY_RELEASE) {
let key = event.get_key_symbol();
if (key == Clutter.KEY_Alt_L || key == Clutter.KEY_Alt_R)
this._sync();
}
return Clutter.EVENT_PROPAGATE;
}
_onLongPress(action, actor, state) {
if (state == Clutter.LongPressState.QUERY ||
state == Clutter.LongPressState.CANCEL)
return true;
this._flipped = !this._flipped;
this._sync();
return true;
}
});
var Indicator = GObject.registerClass( var Indicator = GObject.registerClass(
class Indicator extends PanelMenu.SystemIndicator { class Indicator extends PanelMenu.SystemIndicator {
_init() { _init() {
super._init(); super._init();
let userManager = AccountsService.UserManager.get_default();
this._user = userManager.get_user(GLib.get_user_name());
this._systemActions = new SystemActions.getDefault(); this._systemActions = new SystemActions.getDefault();
this._createSubMenu(); this._createSubMenu();
@ -149,97 +40,36 @@ class Indicator extends PanelMenu.SystemIndicator {
} }
_sessionUpdated() { _sessionUpdated() {
this._settingsAction.visible = Main.sessionMode.allowSettings; this._settingsItem.visible = Main.sessionMode.allowSettings;
} }
_updateMultiUser() { _updateMultiUser() {
let hasSwitchUser = this._loginScreenItem.visible; let hasSwitchUser = this._loginScreenItem.visible;
let hasLogout = this._logoutItem.visible; let hasLogout = this._logoutItem.visible;
this._switchUserSubMenu.visible = hasSwitchUser || hasLogout; this._sessionSubMenu.visible = hasSwitchUser || hasLogout;
}
_updateSwitchUserSubMenu() {
this._switchUserSubMenu.label.text = this._user.get_real_name();
let clutterText = this._switchUserSubMenu.label.clutter_text;
// XXX -- for some reason, the ClutterText's width changes
// rapidly unless we force a relayout of the actor. Probably
// a size cache issue or something. Moving this to be a layout
// manager would be a much better idea.
clutterText.get_allocation_box();
let layout = clutterText.get_layout();
if (layout.is_ellipsized())
this._switchUserSubMenu.label.text = this._user.get_user_name();
}
_createActionButton(iconName, accessibleName) {
return new St.Button({
child: new St.Icon({ icon_name: iconName }),
reactive: true,
can_focus: true,
track_hover: true,
accessible_name: accessibleName,
x_expand: true,
x_align: Clutter.ActorAlign.CENTER,
style_class: 'system-menu-action',
});
} }
_createSubMenu() { _createSubMenu() {
let bindFlags = GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE; let bindFlags = GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE;
let item; let item;
this._switchUserSubMenu = new PopupMenu.PopupSubMenuMenuItem('', true); item = new PopupMenu.PopupImageMenuItem(_('Lock Screen Rotation'),
this._switchUserSubMenu.icon.icon_name = 'avatar-default-symbolic'; this._systemActions.orientation_lock_icon);
// Since the label of the switch user submenu depends on the width of
// the popup menu, and we can't easily connect on allocation-changed
// or notify::width without creating layout cycles, simply update the
// label whenever the menu is opened.
this.menu.connect('open-state-changed', (menu, isOpen) => {
if (isOpen)
this._updateSwitchUserSubMenu();
});
item = new PopupMenu.PopupMenuItem(_("Switch User"));
item.connect('activate', () => { item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateSwitchUser(); this._systemActions.activateLockOrientation();
}); });
this._switchUserSubMenu.menu.addMenuItem(item); this.menu.addMenuItem(item);
this._loginScreenItem = item; this._orientationLockItem = item;
this._systemActions.bind_property('can-switch-user', this._systemActions.bind_property('can-lock-orientation',
this._loginScreenItem, this._orientationLockItem,
'visible', 'visible',
bindFlags); bindFlags);
this._systemActions.connect('notify::orientation-lock-icon', () => {
item = new PopupMenu.PopupMenuItem(_("Log Out")); let iconName = this._systemActions.orientation_lock_icon;
item.connect('activate', () => { this._orientationLockItem.setIcon(iconName);
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateLogout();
}); });
this._switchUserSubMenu.menu.addMenuItem(item);
this._logoutItem = item;
this._systemActions.bind_property('can-logout',
this._logoutItem,
'visible',
bindFlags);
this._switchUserSubMenu.menu.addSettingsAction(_("Account Settings"),
'gnome-user-accounts-panel.desktop');
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( let app = this._settingsApp = Shell.AppSystem.get_default().lookup_app(
'gnome-control-center.desktop' 'gnome-control-center.desktop'
@ -247,83 +77,85 @@ class Indicator extends PanelMenu.SystemIndicator {
if (app) { if (app) {
let [icon, name] = [app.app_info.get_icon().names[0], let [icon, name] = [app.app_info.get_icon().names[0],
app.get_name()]; app.get_name()];
this._settingsAction = this._createActionButton(icon, name); item = new PopupMenu.PopupImageMenuItem(name, icon);
this._settingsAction.connect('clicked', item.connect('activate', () => {
this._onSettingsClicked.bind(this)); this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
this._settingsApp.activate();
});
this.menu.addMenuItem(item);
this._settingsItem = item;
} else { } else {
log('Missing required core component Settings, expect trouble…'); log('Missing required core component Settings, expect trouble…');
this._settingsAction = new St.Widget(); this._settingsItem = new St.Widget();
} }
item.add_child(this._settingsAction);
this._orientationLockAction = this._createActionButton('', _("Orientation Lock")); item = new PopupMenu.PopupImageMenuItem(_('Lock'), 'changes-prevent-symbolic');
this._orientationLockAction.connect('clicked', () => { item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateLockOrientation();
});
item.add_child(this._orientationLockAction);
this._systemActions.bind_property('can-lock-orientation',
this._orientationLockAction,
'visible',
bindFlags);
this._systemActions.bind_property('orientation-lock-icon',
this._orientationLockAction.child,
'icon-name',
bindFlags);
this._lockScreenAction = this._createActionButton('changes-prevent', _("Lock"));
this._lockScreenAction.connect('clicked', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateLockScreen(); this._systemActions.activateLockScreen();
}); });
item.add_child(this._lockScreenAction); this.menu.addMenuItem(item);
this._lockScreenItem = item;
this._systemActions.bind_property('can-lock-screen', this._systemActions.bind_property('can-lock-screen',
this._lockScreenAction, this._lockScreenItem,
'visible', 'visible',
bindFlags); bindFlags);
this._suspendAction = this._createActionButton('media-playback-pause', _("Suspend")); this._sessionSubMenu = new PopupMenu.PopupSubMenuMenuItem(
this._suspendAction.connect('clicked', () => { _('Power Off / Log Out'), true);
this._sessionSubMenu.icon.icon_name = 'system-shutdown-symbolic';
item = new PopupMenu.PopupMenuItem(_("Log Out"));
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateLogout();
});
this._sessionSubMenu.menu.addMenuItem(item);
this._logoutItem = item;
this._systemActions.bind_property('can-logout',
this._logoutItem,
'visible',
bindFlags);
item = new PopupMenu.PopupMenuItem(_("Switch User…"));
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateSwitchUser();
});
this._sessionSubMenu.menu.addMenuItem(item);
this._loginScreenItem = item;
this._systemActions.bind_property('can-switch-user',
this._loginScreenItem,
'visible',
bindFlags);
this._sessionSubMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
item = new PopupMenu.PopupMenuItem(_("Suspend"));
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateSuspend(); this._systemActions.activateSuspend();
}); });
this._sessionSubMenu.menu.addMenuItem(item);
this._suspendItem = item;
this._systemActions.bind_property('can-suspend', this._systemActions.bind_property('can-suspend',
this._suspendAction, this._suspendItem,
'visible', 'visible',
bindFlags); bindFlags);
this._powerOffAction = this._createActionButton('system-shutdown', _("Power Off")); item = new PopupMenu.PopupMenuItem(_("Power Off…"));
this._powerOffAction.connect('clicked', () => { item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activatePowerOff(); this._systemActions.activatePowerOff();
}); });
this._sessionSubMenu.menu.addMenuItem(item);
this._powerOffItem = item;
this._systemActions.bind_property('can-power-off', this._systemActions.bind_property('can-power-off',
this._powerOffAction, this._powerOffItem,
'visible', 'visible',
bindFlags); bindFlags);
this._altSwitcher = new AltSwitcher(this._powerOffAction, this._suspendAction); this.menu.addMenuItem(this._sessionSubMenu);
item.add_child(this._altSwitcher);
this.menu.addMenuItem(item);
let visibilityGroup = [
this._settingsAction,
this._orientationLockAction,
this._lockScreenAction,
this._altSwitcher,
];
for (let actor of visibilityGroup) {
actor.connect('notify::visible', () => {
this.buttonGroup.visible = visibilityGroup.some(a => a.visible);
});
}
}
_onSettingsClicked() {
this.menu.itemActivated();
Main.overview.hide();
this._settingsApp.activate();
} }
}); });