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 || const LockItem = GObject.registerClass(
this._powerOffItem.visible; class LockItem extends QuickSettingsItem {
} _init() {
this._systemActions = new SystemActions.getDefault();
_createSubMenu() {
let bindFlags = GObject.BindingFlags.DEFAULT | GObject.BindingFlags.SYNC_CREATE; super._init({
let item; style_class: 'icon-button',
can_focus: true,
let app = this._settingsApp = Shell.AppSystem.get_default().lookup_app( child: new St.Icon({
'org.gnome.Settings.desktop'); icon_name: 'system-lock-screen-symbolic',
if (app) { }),
const [icon] = app.app_info.get_icon().names; accessible_name: _('Lock Screen'),
const name = app.app_info.get_name(); });
item = new PopupMenu.PopupImageMenuItem(name, icon);
item.connect('activate', () => { this._systemActions.bind_property('can-lock-screen',
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); this, 'visible',
Main.overview.hide(); GObject.BindingFlags.DEFAULT |
this._settingsApp.activate(); GObject.BindingFlags.SYNC_CREATE);
});
this.menu.addMenuItem(item); this.connect('clicked',
this._settingsItem = item; () => this._systemActions.activateLockScreen());
} else { }
log('Missing required core component Settings, expect trouble…'); });
this._settingsItem = new St.Widget();
}
const SystemItem = GObject.registerClass(
item = new PopupMenu.PopupImageMenuItem(_('Lock'), 'changes-prevent-symbolic'); class SystemItem extends QuickSettingsItem {
item.connect('activate', () => { _init() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); super._init({
this._systemActions.activateLockScreen(); style_class: 'quick-settings-system-item',
}); reactive: false,
this.menu.addMenuItem(item); });
this._lockScreenItem = item;
this._systemActions.bind_property('can-lock-screen', this.child = new St.BoxLayout();
this._lockScreenItem, 'visible',
bindFlags); // spacer
this.child.add_child(new Clutter.Actor({x_expand: true}));
this._sessionSubMenu = new PopupMenu.PopupSubMenuMenuItem(
_('Power Off / Log Out'), true); const settingsItem = new SettingsItem();
this._sessionSubMenu.icon.icon_name = 'system-shutdown-symbolic'; this.child.add_child(settingsItem);
item = new PopupMenu.PopupMenuItem(_('Suspend')); const lockItem = new LockItem();
item.connect('activate', () => { this.child.add_child(lockItem);
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activateSuspend(); const shutdownItem = new ShutdownItem();
}); this.child.add_child(shutdownItem);
this._sessionSubMenu.menu.addMenuItem(item);
this._suspendItem = item; this.menu = shutdownItem.menu;
this._systemActions.bind_property('can-suspend', }
this._suspendItem, 'visible', });
bindFlags);
var Indicator = GObject.registerClass(
item = new PopupMenu.PopupMenuItem(_('Restart…')); class Indicator extends SystemIndicator {
item.connect('activate', () => { _init() {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE); super._init();
this._systemActions.activateRestart();
}); const item = new SystemItem();
this._sessionSubMenu.menu.addMenuItem(item);
this._restartItem = item; this.quickSettingsItems.push(item);
this._systemActions.bind_property('can-restart',
this._restartItem, 'visible',
bindFlags);
item = new PopupMenu.PopupMenuItem(_('Power Off…'));
item.connect('activate', () => {
this.menu.itemActivated(BoxPointer.PopupAnimation.NONE);
this._systemActions.activatePowerOff();
});
this._sessionSubMenu.menu.addMenuItem(item);
this._powerOffItem = item;
this._systemActions.bind_property('can-power-off',
this._powerOffItem, 'visible',
bindFlags);
this._sessionSubMenu.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
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.menu.addMenuItem(this._sessionSubMenu);
} }
}); });