status/powerProfiles: Add power mode selection

Settings' power panel gained support for switchable power profiles
in GNOME 40. It's useful to have that functionality more readily
available, so expose it in the system status menu as well.

https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/3944

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/1907>
This commit is contained in:
Florian Müllner
2021-07-07 22:05:25 +02:00
parent 770f15207c
commit f1320bd250
6 changed files with 194 additions and 0 deletions

View File

@ -133,6 +133,7 @@
<file>ui/status/nightLight.js</file>
<file>ui/status/network.js</file>
<file>ui/status/power.js</file>
<file>ui/status/powerProfiles.js</file>
<file>ui/status/rfkill.js</file>
<file>ui/status/volume.js</file>
<file>ui/status/bluetooth.js</file>

View File

@ -684,6 +684,7 @@ class AggregateMenu extends PanelMenu.Button {
this._remoteAccess = new imports.ui.status.remoteAccess.RemoteAccessApplet();
this._power = new imports.ui.status.power.Indicator();
this._powerProfiles = new imports.ui.status.powerProfiles.Indicator();
this._rfkill = new imports.ui.status.rfkill.Indicator();
this._volume = new imports.ui.status.volume.Indicator();
this._brightness = new imports.ui.status.brightness.Indicator();
@ -703,6 +704,7 @@ class AggregateMenu extends PanelMenu.Button {
this._indicators.add_child(this._rfkill);
this._indicators.add_child(this._volume);
this._indicators.add_child(this._power);
this._indicators.add_child(this._powerProfiles);
this.menu.addMenuItem(this._volume.menu);
this.menu.addMenuItem(this._brightness.menu);
@ -717,6 +719,7 @@ class AggregateMenu extends PanelMenu.Button {
this.menu.addMenuItem(this._location.menu);
this.menu.addMenuItem(this._rfkill.menu);
this.menu.addMenuItem(this._power.menu);
this.menu.addMenuItem(this._powerProfiles.menu);
this.menu.addMenuItem(this._nightLight.menu);
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this.menu.addMenuItem(this._system.menu);
@ -724,6 +727,7 @@ class AggregateMenu extends PanelMenu.Button {
menuLayout.addSizeChild(this._location.menu.actor);
menuLayout.addSizeChild(this._rfkill.menu.actor);
menuLayout.addSizeChild(this._power.menu.actor);
menuLayout.addSizeChild(this._powerProfiles.menu.actor);
menuLayout.addSizeChild(this._system.menu.actor);
}
});

View File

@ -0,0 +1,111 @@
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
/* exported Indicator */
const { Gio, GObject } = imports.gi;
const Main = imports.ui.main;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const { loadInterfaceXML } = imports.misc.fileUtils;
const BUS_NAME = 'net.hadess.PowerProfiles';
const OBJECT_PATH = '/net/hadess/PowerProfiles';
const PowerProfilesIface = loadInterfaceXML('net.hadess.PowerProfiles');
const PowerProfilesProxy = Gio.DBusProxy.makeProxyWrapper(PowerProfilesIface);
const PROFILE_LABELS = {
'performance': _('Performance Mode'),
'balanced': _('Balanced Power'),
'power-saver': _('Power Saver'),
};
const PROFILE_ICONS = {
'performance': 'power-profile-performance-symbolic',
'balanced': 'power-profile-balanced-symbolic',
'power-saver': 'power-profile-power-saver-symbolic',
};
var Indicator = GObject.registerClass(
class Indicator extends PanelMenu.SystemIndicator {
_init() {
super._init();
this._profileItems = new Map();
this._updateProfiles = true;
this._proxy = new PowerProfilesProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
(proxy, error) => {
if (error) {
log(error.message);
} else {
this._proxy.connect('g-properties-changed',
(p, properties) => {
const propertyNames = properties.deep_unpack();
this._updateProfiles = 'Profiles' in propertyNames;
this._sync();
});
}
this._sync();
});
this._item = new PopupMenu.PopupSubMenuMenuItem('', true);
this._profileSection = new PopupMenu.PopupMenuSection();
this._item.menu.addMenuItem(this._profileSection);
this._item.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._item.menu.addSettingsAction(_('Power Settings'),
'gnome-power-panel.desktop');
this.menu.addMenuItem(this._item);
Main.sessionMode.connect('updated', this._sessionUpdated.bind(this));
this._sessionUpdated();
this._sync();
}
_sessionUpdated() {
const sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
this.menu.setSensitive(sensitive);
}
_sync() {
this._item.visible = this._proxy.g_name_owner !== null;
if (!this._item.visible)
return;
if (this._updateProfiles) {
this._profileSection.removeAll();
this._profileItems.clear();
const profiles = this._proxy.Profiles
.map(p => p.Profile.unpack())
.reverse();
for (const profile of profiles) {
const label = PROFILE_LABELS[profile];
if (!label)
continue;
const item = new PopupMenu.PopupMenuItem(label);
item.connect('activate',
() => (this._proxy.ActiveProfile = profile));
this._profileItems.set(profile, item);
this._profileSection.addMenuItem(item);
}
this._updateProfiles = false;
}
for (const [profile, item] of this._profileItems) {
item.setOrnament(profile === this._proxy.ActiveProfile
? PopupMenu.Ornament.DOT
: PopupMenu.Ornament.NONE);
}
const perfItem = this._profileItems.get('performance');
if (perfItem)
perfItem.sensitive = this._proxy.PerformanceInhibited === '';
this._item.label.text = PROFILE_LABELS[this._proxy.ActiveProfile];
this._item.icon.icon_name = PROFILE_ICONS[this._proxy.ActiveProfile];
}
});