status/system: Port to quick settings

This port is a bit messier than the previous ones, because the
existing menu section translates less directly to the new UI,
which uses a row of individual toggles for settings, lock and
shutdown.

In order to not complicate the grid layout further by supporting
rows with a different number of columns than the overall grid and
children at their natural size, create a custom, non-reactive
SystemItem item that spans an entire row, and contains the individual
toggles.

This works quite well, even with the shutdown item that uses a menu
for the various actions.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2393>
This commit is contained in:
Florian Müllner 2022-07-30 02:17:15 +02:00 committed by Marge Bot
parent 62c62eced0
commit ea00da7fd7
3 changed files with 160 additions and 136 deletions

View File

@ -83,3 +83,7 @@
.quick-toggle-menu-container { .quick-toggle-menu-container {
padding: 2 * $base_padding $base_padding 0; padding: 2 * $base_padding $base_padding 0;
} }
.quick-settings-system-item {
& > StBoxLayout { spacing: 2 * $base_padding; }
}

View File

@ -378,18 +378,11 @@ class AggregateMenu extends PanelMenu.Button {
else else
this._network = null; this._network = null;
this._system = new imports.ui.status.system.Indicator();
if (this._network) if (this._network)
this._indicators.add_child(this._network); this._indicators.add_child(this._network);
if (this._network) if (this._network)
this.menu.addMenuItem(this._network.menu); this.menu.addMenuItem(this._network.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._system.menu);
menuLayout.addSizeChild(this._system.menu.actor);
} }
}); });
@ -410,6 +403,7 @@ class QuickSettings extends PanelMenu.Button {
else else
this._bluetooth = null; this._bluetooth = null;
this._system = new imports.ui.status.system.Indicator();
this._volume = new imports.ui.status.volume.Indicator(); this._volume = new imports.ui.status.volume.Indicator();
this._brightness = new imports.ui.status.brightness.Indicator(); this._brightness = new imports.ui.status.brightness.Indicator();
this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet(); this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet();
@ -437,7 +431,9 @@ class QuickSettings extends PanelMenu.Button {
this._indicators.add_child(this._volume); this._indicators.add_child(this._volume);
this._indicators.add_child(this._unsafeMode); this._indicators.add_child(this._unsafeMode);
this._indicators.add_child(this._power); this._indicators.add_child(this._power);
this._indicators.add_child(this._system);
this._addItems(this._system.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);
this._addItems(this._volume.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS); this._addItems(this._volume.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);
this._addItems(this._brightness.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS); this._addItems(this._brightness.quickSettingsItems, N_QUICK_SETTINGS_COLUMNS);

View File

@ -1,157 +1,181 @@
// -*- 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 { GObject, Shell, St } = imports.gi; const {Clutter, GObject, Shell, St} = imports.gi;
const BoxPointer = imports.ui.boxpointer;
const SystemActions = imports.misc.systemActions; const SystemActions = imports.misc.systemActions;
const Main = imports.ui.main; const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu; const PopupMenu = imports.ui.popupMenu;
const {QuickSettingsItem, SystemIndicator} = imports.ui.quickSettings;
var Indicator = GObject.registerClass( const SettingsItem = GObject.registerClass(
class Indicator extends PanelMenu.SystemIndicator { class SettingsItem extends QuickSettingsItem {
_init() { _init() {
super._init(); super._init({
style_class: 'icon-button',
can_focus: true,
child: new St.Icon(),
});
this._settingsApp = Shell.AppSystem.get_default().lookup_app(
'org.gnome.Settings.desktop');
if (!this._settingsApp)
console.warn('Missing required core component Settings, expect trouble…');
this.child.gicon = this._settingsApp?.get_icon() ?? null;
this.accessible_name = this._settingsApp?.get_name() ?? null;
this.connect('clicked', () => {
Main.overview.hide();
Main.panel.closeQuickSettings();
this._settingsApp.activate();
});
Main.sessionMode.connectObject('updated', () => this._sync(), this);
this._sync();
}
_sync() {
this.visible =
this._settingsApp != null && Main.sessionMode.allowSettings;
}
});
const ShutdownItem = GObject.registerClass(
class ShutdownItem extends QuickSettingsItem {
_init() {
super._init({
style_class: 'icon-button',
hasMenu: true,
canFocus: true,
child: new St.Icon({
icon_name: 'system-shutdown-symbolic',
}),
accessible_name: _('Power Off Menu'),
});
this._systemActions = new SystemActions.getDefault(); this._systemActions = new SystemActions.getDefault();
this._items = [];
this._createSubMenu(); this.menu.setHeader('system-shutdown-symbolic', 'Power Off');
this._addSystemAction(_('Suspend'), 'can-suspend', () => {
this._systemActions.activateSuspend();
Main.panel.closeQuickSettings();
});
this._addSystemAction(_('Restart…'), 'can-restart', () => {
this._systemActions.activateRestart();
Main.panel.closeQuickSettings();
});
this._addSystemAction(_('Power Off…'), 'can-power-off', () => {
this._systemActions.activatePowerOff();
Main.panel.closeQuickSettings();
});
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._addSystemAction(_('Log Out'), 'can-logout', () => {
this._systemActions.activateLogout();
Main.panel.closeQuickSettings();
});
this._addSystemAction(_('Switch User…'), 'can-switch-user', () => {
this._systemActions.activateSwitchUser();
Main.panel.closeQuickSettings();
});
this._loginScreenItem.connect('notify::visible',
() => this._updateSessionSubMenu());
this._logoutItem.connect('notify::visible',
() => this._updateSessionSubMenu());
this._suspendItem.connect('notify::visible',
() => this._updateSessionSubMenu());
this._powerOffItem.connect('notify::visible',
() => this._updateSessionSubMenu());
this._restartItem.connect('notify::visible',
() => this._updateSessionSubMenu());
// Whether shutdown is available or not depends on both lockdown // Whether shutdown is available or not depends on both lockdown
// settings (disable-log-out) and Polkit policy - the latter doesn't // settings (disable-log-out) and Polkit policy - the latter doesn't
// notify, so we update the menu item each time the menu opens or // notify, so we update the item each time we become visible or
// the lockdown setting changes, which should be close enough. // the lockdown setting changes, which should be close enough.
this.menu.connect('open-state-changed', (menu, open) => { this.connect('notify::mapped', () => {
if (!open) if (!this.mapped)
return; return;
this._systemActions.forceUpdate(); this._systemActions.forceUpdate();
}); });
this._updateSessionSubMenu();
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this)); this.connect('clicked', () => this.menu.open());
this._sessionUpdated(); this.connect('popup-menu', () => this.menu.open());
} }
_sessionUpdated() { _addSystemAction(label, propName, callback) {
this._settingsItem.visible = Main.sessionMode.allowSettings; const item = this.menu.addAction(label, callback);
this._items.push(item);
this._systemActions.bind_property(propName,
item, 'visible',
GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE);
item.connect('notify::visible', () => this._sync());
} }
_updateSessionSubMenu() { _sync() {
this._sessionSubMenu.visible = this.visible = this._items.some(i => i.visible);
this._loginScreenItem.visible ||
this._logoutItem.visible ||
this._suspendItem.visible ||
this._restartItem.visible ||
this._powerOffItem.visible;
} }
_createSubMenu() {
let bindFlags = GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE;
let item;
let app = this._settingsApp = Shell.AppSystem.get_default().lookup_app(
'org.gnome.Settings.desktop');
if (app) {
const [icon] = app.app_info.get_icon().names;
const name = app.app_info.get_name();
item = new PopupMenu.PopupImageMenuItem(name, icon);
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
Main.overview.hide();
this._settingsApp.activate();
}); });
this.menu.addMenuItem(item);
this._settingsItem = item;
} else {
log('Missing required core component Settings, expect trouble…');
this._settingsItem = new St.Widget();
}
item = new PopupMenu.PopupImageMenuItem(_('Lock'), 'changes-prevent-symbolic'); const LockItem = GObject.registerClass(
item.connect('activate', () => { class LockItem extends QuickSettingsItem {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); _init() {
this._systemActions.activateLockScreen(); this._systemActions = new SystemActions.getDefault();
super._init({
style_class: 'icon-button',
can_focus: true,
child: new St.Icon({
icon_name: 'system-lock-screen-symbolic',
}),
accessible_name: _('Lock Screen'),
}); });
this.menu.addMenuItem(item);
this._lockScreenItem = item;
this._systemActions.bind_property('can-lock-screen', this._systemActions.bind_property('can-lock-screen',
this._lockScreenItem, 'visible', this, 'visible',
bindFlags); GObject.BindingFlags.DEFAULT |
GObject.BindingFlags.SYNC_CREATE);
this._sessionSubMenu = new PopupMenu.PopupSubMenuMenuItem( this.connect('clicked',
_('Power Off / Log Out'), true); () => this._systemActions.activateLockScreen());
this._sessionSubMenu.icon.icon_name = 'system-shutdown-symbolic'; }
});
item = new PopupMenu.PopupMenuItem(_('Suspend'));
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); const SystemItem = GObject.registerClass(
this._systemActions.activateSuspend(); class SystemItem extends QuickSettingsItem {
}); _init() {
this._sessionSubMenu.menu.addMenuItem(item); super._init({
this._suspendItem = item; style_class: 'quick-settings-system-item',
this._systemActions.bind_property('can-suspend', reactive: false,
this._suspendItem, 'visible', });
bindFlags);
this.child = new St.BoxLayout();
item = new PopupMenu.PopupMenuItem(_('Restart…'));
item.connect('activate', () => { // spacer
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.child.add_child(new Clutter.Actor({x_expand: true}));
this._systemActions.activateRestart();
}); const settingsItem = new SettingsItem();
this._sessionSubMenu.menu.addMenuItem(item); this.child.add_child(settingsItem);
this._restartItem = item;
this._systemActions.bind_property('can-restart', const lockItem = new LockItem();
this._restartItem, 'visible', this.child.add_child(lockItem);
bindFlags);
const shutdownItem = new ShutdownItem();
item = new PopupMenu.PopupMenuItem(_('Power Off…')); this.child.add_child(shutdownItem);
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this.menu = shutdownItem.menu;
this._systemActions.activatePowerOff(); }
}); });
this._sessionSubMenu.menu.addMenuItem(item);
this._powerOffItem = item; var Indicator = GObject.registerClass(
this._systemActions.bind_property('can-power-off', class Indicator extends SystemIndicator {
this._powerOffItem, 'visible', _init() {
bindFlags); super._init();
this._sessionSubMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); const item = new SystemItem();
item = new PopupMenu.PopupMenuItem(_('Log Out')); this.quickSettingsItems.push(item);
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.menu.addMenuItem(this._sessionSubMenu);
} }
}); });