status: Port to a new SystemIndicator framework

We can't silently replace the old behavior of separate status
icons into a new system. Replace SystemStatusButton with a new
SystemIndicator class which will allow for the flexibility we
need. For now, make it a subclass of Button so that it mostly
feels the same, but we'll soon be swapping it out with a dummy
implementation that the aggregate menu will use.

I think the code cleanup here is worth it.

https://bugzilla.gnome.org/show_bug.cgi?id=705845
This commit is contained in:
Jasper St. Pierre 2013-06-06 17:27:25 -04:00
parent a347f02545
commit 5cca26a565
9 changed files with 62 additions and 74 deletions

View File

@ -614,7 +614,7 @@ StScrollBar StButton#vhandle:active {
-boxpointer-gap: 4px; -boxpointer-gap: 4px;
} }
.panel-status-button-box, .panel-status-indicators-box,
.panel-status-menu-box { .panel-status-menu-box {
spacing: 4px; spacing: 4px;
} }

View File

@ -29,7 +29,7 @@ const PopupMenu = imports.ui.popupMenu;
const PowerMenuButton = new Lang.Class({ const PowerMenuButton = new Lang.Class({
Name: 'PowerMenuButton', Name: 'PowerMenuButton',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
/* Translators: accessible name of the power menu in the login screen */ /* Translators: accessible name of the power menu in the login screen */

View File

@ -233,51 +233,29 @@ const Button = new Lang.Class({
}); });
Signals.addSignalMethods(Button.prototype); Signals.addSignalMethods(Button.prototype);
/* SystemStatusButton: /* SystemIndicator:
* *
* This class manages one System Status indicator (network, keyboard, * This class manages one system indicator, which are the icons
* volume, bluetooth...), which is just a PanelMenuButton with an * that you see at the top right. A system indicator is composed
* icon. * of an icon and a menu section, which will be composed into the
* aggregate menu.
*/ */
const SystemStatusButton = new Lang.Class({ const SystemIndicator = new Lang.Class({
Name: 'SystemStatusButton', Name: 'SystemIndicator',
Extends: Button, Extends: Button,
_init: function(iconName, nameText) { _init: function() {
this.parent(0.0, nameText); this.parent(0.0);
this.actor.add_style_class_name('panel-status-button'); this.actor.add_style_class_name('panel-status-button');
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' }); this.indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
this.actor.add_actor(this._box); this.actor.add_actor(this.indicators);
if (iconName)
this.setIcon(iconName);
}, },
get icons() { addIndicator: function(gicon) {
return this._box.get_children();
},
addIcon: function(gicon) {
let icon = new St.Icon({ gicon: gicon, let icon = new St.Icon({ gicon: gicon,
style_class: 'system-status-icon' }); style_class: 'system-status-icon' });
this._box.add_actor(icon); this.indicators.add_actor(icon);
this.emit('icons-changed');
return icon; return icon;
},
setIcon: function(iconName) {
if (!this.mainIcon)
this.mainIcon = this.addIcon(null);
this.mainIcon.icon_name = iconName;
},
setGIcon: function(gicon) {
if (this.mainIcon)
this.mainIcon.gicon = gicon;
else
this.mainIcon = this.addIcon(gicon);
} }
}); });

View File

@ -15,10 +15,12 @@ const PopupMenu = imports.ui.popupMenu;
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'BTIndicator', Name: 'BTIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent('bluetooth-disabled-symbolic', _("Bluetooth")); this.parent();
this._indicator = this.addIndicator(new Gio.ThemedIcon({ name: 'bluetooth-active-symbolic' }));
// The Bluetooth menu only appears when Bluetooth is in use, // The Bluetooth menu only appears when Bluetooth is in use,
// so just statically build it with a "Turn Off" menu item. // so just statically build it with a "Turn Off" menu item.
@ -47,8 +49,8 @@ const Indicator = new Lang.Class({
let nDevices = connectedDevices.length; let nDevices = connectedDevices.length;
let on = nDevices > 0; let on = nDevices > 0;
this.mainIcon.visible = on; this._indicator.visible = on;
this.actor.visible = on; this._item.actor.visible = on;
if (on) if (on)
this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices); this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices").format(nDevices);

View File

@ -37,26 +37,26 @@ const FakeStatusIcon = new Lang.Class({
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'LockScreenMenuIndicator', Name: 'LockScreenMenuIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent(null, _("Volume, network, battery")); this.parent(null, _("Volume, network, battery"));
this._box.style_class = 'lock-screen-status-button-box'; this.indicators.style_class = 'lock-screen-status-button-box';
this._volumeControl = VolumeMenu.getMixerControl(); this._volumeControl = VolumeMenu.getMixerControl();
this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl); this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
this.menu.addMenuItem(this._volumeMenu); this.menu.addMenuItem(this._volumeMenu);
this._volume = new FakeStatusIcon(Main.panel.statusArea.volume); this._volume = new FakeStatusIcon(Main.panel.statusArea.volume);
this._box.add_child(this._volume.actor); this.indicators.add_child(this._volume.actor);
// Network may not exist if the user doesn't have NetworkManager // Network may not exist if the user doesn't have NetworkManager
if (Main.panel.statusArea.network) { if (Main.panel.statusArea.network) {
this._network = new FakeStatusIcon(Main.panel.statusArea.network); this._network = new FakeStatusIcon(Main.panel.statusArea.network);
this._box.add_child(this._network.actor); this.indicators.add_child(this._network.actor);
} }
this._battery = new FakeStatusIcon(Main.panel.statusArea.battery); this._battery = new FakeStatusIcon(Main.panel.statusArea.battery);
this._box.add_child(this._battery.actor); this.indicators.add_child(this._battery.actor);
} }
}); });

View File

@ -1152,12 +1152,13 @@ Signals.addSignalMethods(NMVPNSection.prototype);
const NMApplet = new Lang.Class({ const NMApplet = new Lang.Class({
Name: 'NMApplet', Name: 'NMApplet',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent('network-offline-symbolic', _('Network')); this.parent();
this._vpnIcon = this.addIcon(null); this._primaryIndicator = this.addIndicator(null);
this._vpnIndicator = this.addIndicator(null);
// Device types // Device types
this._dtypes = { }; this._dtypes = { };
@ -1579,29 +1580,27 @@ const NMApplet = new Lang.Class({
}, },
_syncNMState: function() { _syncNMState: function() {
this.mainIcon.visible = this._client.manager_running;
this.actor.visible = this.mainIcon.visible;
this._syncActiveConnections(); this._syncActiveConnections();
this.indicators.visible = this._client.manager_running;
this._section.actor.visible = this._client.networking_enabled; this._section.actor.visible = this._client.networking_enabled;
}, },
_updateIcon: function() { _updateIcon: function() {
let hasApIcon = false; let mc = this._mainConnection;
let hasMobileIcon = false;
if (!this._client.networking_enabled || !this._mainConnection) { if (!this._client.networking_enabled || !mc) {
this.setIcon('network-offline-symbolic'); this._primaryIndicator.icon_name = 'network-offline-symbolic';
} else { } else {
let dev = this._mainConnection._primaryDevice; let dev = this._mainConnection._primaryDevice;
if (!dev) { if (!dev) {
log('Active connection with no primary device?'); log('Active connection with no primary device?');
return; return;
} }
this.setIcon(dev.getIndicatorIcon()); this._primaryIndicator.icon_name = dev.getIndicatorIcon(mc);
} }
this._vpnIcon.icon_name = this._vpnSection.getIndicatorIcon(); this._vpnIndicator.icon_name = this._vpnSection.getIndicatorIcon();
this._vpnIcon.visible = (this._vpnIcon.icon_name != ''); this._vpnIndicator.visible = (this._vpnIndicator.icon_name != '');
} }
}); });

View File

@ -25,10 +25,12 @@ const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface);
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'PowerIndicator', Name: 'PowerIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent('battery-missing-symbolic', _("Battery")); this.parent();
this._indicator = this.addIndicator(null);
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
Lang.bind(this, function(proxy, error) { Lang.bind(this, function(proxy, error) {
@ -104,16 +106,14 @@ const Indicator = new Lang.Class({
_syncIcon: function() { _syncIcon: function() {
let icon = this._proxy.Icon; let icon = this._proxy.Icon;
let hasIcon = false;
if (icon) { if (icon) {
let gicon = Gio.icon_new_for_string(icon); let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon); this._indicator.gicon = gicon;
this._indicator.show();
this._item.icon.gicon = gicon; this._item.icon.gicon = gicon;
hasIcon = true; } else {
this._indicator.hide();
} }
this.mainIcon.visible = hasIcon;
this.actor.visible = hasIcon;
}, },
_sync: function() { _sync: function() {

View File

@ -42,10 +42,12 @@ const SystemdLoginSession = Gio.DBusProxy.makeProxyWrapper(SystemdLoginSessionIf
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'SystemIndicator', Name: 'SystemIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent('system-shutdown-symbolic', _("System")); this.parent();
this._indicator = this.addIndicator(new Gio.ThemedIcon({ name: 'system-shutdown-symbolic' }));
this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA }); this._screenSaverSettings = new Gio.Settings({ schema: SCREENSAVER_SCHEMA });
this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA }); this._lockdownSettings = new Gio.Settings({ schema: LOCKDOWN_SCHEMA });

View File

@ -296,22 +296,29 @@ const VolumeMenu = new Lang.Class({
const Indicator = new Lang.Class({ const Indicator = new Lang.Class({
Name: 'VolumeIndicator', Name: 'VolumeIndicator',
Extends: PanelMenu.SystemStatusButton, Extends: PanelMenu.SystemIndicator,
_init: function() { _init: function() {
this.parent('audio-volume-muted-symbolic', _("Volume")); this.parent();
this._primaryIndicator = this.addIndicator(null);
this._control = getMixerControl(); this._control = getMixerControl();
this._volumeMenu = new VolumeMenu(this._control); this._volumeMenu = new VolumeMenu(this._control);
this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) { this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu) {
let icon = this._volumeMenu.getIcon(); let icon = this._volumeMenu.getIcon();
this.actor.visible = (icon != null);
this.setIcon(icon); if (icon != null) {
this.indicators.show();
this._primaryIndicator.icon_name = icon;
} else {
this.indicators.hide();
}
})); }));
this.menu.addMenuItem(this._volumeMenu); this.menu.addMenuItem(this._volumeMenu);
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); this.indicators.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
}, },
_onScrollEvent: function(actor, event) { _onScrollEvent: function(actor, event) {