[gnome-shell] Add a new lock screen menu to combine volume network and power
- From: Giovanni Campagna <gcampagna src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] Add a new lock screen menu to combine volume network and power
- Date: Tue, 28 Aug 2012 23:16:00 +0000 (UTC)
commit c1de2788b1714cf11d2bf380dcf9c3681916a01f
Author: Giovanni Campagna <gcampagna src gnome org>
Date: Sun Aug 26 16:05:46 2012 +0200
Add a new lock screen menu to combine volume network and power
The design has a combined volume-network-power indicator in the lock
screen, which when opened shows a volume slider. Implement it by abstracting
the volume menu into a PopupMenuSection, and by creating three StIcons
bound to the real ones.
https://bugzilla.gnome.org/show_bug.cgi?id=682540
js/Makefile.am | 1 +
js/ui/panel.js | 12 ++--
js/ui/panelMenu.js | 3 +-
js/ui/sessionMode.js | 11 +++--
js/ui/status/lockScreenMenu.js | 62 +++++++++++++++++++++++++
js/ui/status/network.js | 30 ++++--------
js/ui/status/power.js | 22 +++++----
js/ui/status/volume.js | 99 ++++++++++++++++++++++++++++------------
8 files changed, 172 insertions(+), 68 deletions(-)
---
diff --git a/js/Makefile.am b/js/Makefile.am
index 92e0e59..3300981 100644
--- a/js/Makefile.am
+++ b/js/Makefile.am
@@ -85,6 +85,7 @@ nobase_dist_js_DATA = \
ui/shellDBus.js \
ui/status/accessibility.js \
ui/status/keyboard.js \
+ ui/status/lockScreenMenu.js \
ui/status/network.js \
ui/status/power.js \
ui/status/volume.js \
diff --git a/js/ui/panel.js b/js/ui/panel.js
index 943079f..631a0a1 100644
--- a/js/ui/panel.js
+++ b/js/ui/panel.js
@@ -908,7 +908,7 @@ const Panel = new Lang.Class({
reactive: true });
this.actor._delegate = this;
- this._statusArea = {};
+ this.statusArea = {};
Main.overview.connect('shown', Lang.bind(this, function () {
this.actor.add_style_class_name('in-overview');
@@ -1126,7 +1126,7 @@ const Panel = new Lang.Class({
},
addToStatusArea: function(role, indicator, position) {
- if (this._statusArea[role])
+ if (this.statusArea[role])
throw new Error('Extension point conflict: there is already a status indicator for role ' + role);
if (!(indicator instanceof PanelMenu.Button))
@@ -1138,9 +1138,9 @@ const Panel = new Lang.Class({
if (indicator.menu)
this._menus.addMenu(indicator.menu);
- this._statusArea[role] = indicator;
+ this.statusArea[role] = indicator;
let destroyId = indicator.connect('destroy', Lang.bind(this, function(emitter) {
- delete this._statusArea[role];
+ delete this.statusArea[role];
emitter.disconnect(destroyId);
}));
@@ -1155,7 +1155,7 @@ const Panel = new Lang.Class({
if (this._dateMenu)
this._dateMenu.setLockedState(locked);
- for (let id in this._statusArea)
- this._statusArea[id].setLockedState(locked);
+ for (let id in this.statusArea)
+ this.statusArea[id].setLockedState(locked);
},
});
diff --git a/js/ui/panelMenu.js b/js/ui/panelMenu.js
index 29007bf..3e48f62 100644
--- a/js/ui/panelMenu.js
+++ b/js/ui/panelMenu.js
@@ -236,7 +236,8 @@ const SystemStatusButton = new Lang.Class({
this._box = new St.BoxLayout({ style_class: 'panel-status-button-box' });
this.actor.add_actor(this._box);
- this.setIcon(iconName);
+ if (iconName)
+ this.setIcon(iconName);
},
addIcon: function(gicon) {
diff --git a/js/ui/sessionMode.js b/js/ui/sessionMode.js
index 1cc4ec3..55cfed7 100644
--- a/js/ui/sessionMode.js
+++ b/js/ui/sessionMode.js
@@ -11,6 +11,7 @@ const STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION = {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
+ 'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'userMenu': imports.ui.userMenu.UserMenuButton
};
@@ -44,12 +45,13 @@ const _modes = {
statusArea: {
order: [
'a11y', 'display', 'keyboard',
- 'volume', 'battery', 'powerMenu'
+ 'volume', 'battery', 'lockScreen', 'powerMenu'
],
implementation: {
'a11y': imports.ui.status.accessibility.ATIndicator,
'volume': imports.ui.status.volume.Indicator,
'battery': imports.ui.status.power.Indicator,
+ 'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
'keyboard': imports.ui.status.keyboard.InputSourceIndicator,
'powerMenu': imports.gdm.powerMenu.PowerMenuButton
}
@@ -68,12 +70,13 @@ const _modes = {
extraStylesheet: null,
statusArea: {
order: [
- 'a11y', 'keyboard', 'volume'
+ 'a11y', 'keyboard', 'volume', 'lockScreen',
],
implementation: {
'a11y': imports.ui.status.accessibility.ATIndicator,
'keyboard': imports.ui.status.keyboard.XKBIndicator,
- 'volume': imports.ui.status.volume.Indicator
+ 'volume': imports.ui.status.volume.Indicator,
+ 'lockScreen': imports.ui.status.lockScreenMenu.Indicator,
}
}
},
@@ -92,7 +95,7 @@ const _modes = {
statusArea: {
order: [
'input-method', 'a11y', 'keyboard', 'volume', 'bluetooth',
- 'network', 'battery', 'userMenu'
+ 'network', 'battery', 'lockScreen', 'userMenu'
],
implementation: STANDARD_STATUS_AREA_SHELL_IMPLEMENTATION
}
diff --git a/js/ui/status/lockScreenMenu.js b/js/ui/status/lockScreenMenu.js
new file mode 100644
index 0000000..6f8b897
--- /dev/null
+++ b/js/ui/status/lockScreenMenu.js
@@ -0,0 +1,62 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const GObject = imports.gi.GObject;
+const Lang = imports.lang;
+const St = imports.gi.St;
+
+const Main = imports.ui.main;
+const PanelMenu = imports.ui.panelMenu;
+const PopupMenu = imports.ui.popupMenu;
+const VolumeMenu = imports.ui.status.volume;
+
+const Indicator = new Lang.Class({
+ Name: 'LockScreenMenuIndicator',
+ Extends: PanelMenu.SystemStatusButton,
+
+ _init: function() {
+ this.parent(null, _("Volume, network, battery"));
+ this.actor.hide();
+
+ this._volume = Main.panel.statusArea.volume;
+ if (this._volume) {
+ this._volumeIcon = this.addIcon(null);
+ this._volume.mainIcon.bind_property('gicon', this._volumeIcon, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._volume.mainIcon.bind_property('visible', this._volumeIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._volumeControl = VolumeMenu.getMixerControl();
+ this._volumeMenu = new VolumeMenu.VolumeMenu(this._volumeControl);
+ this.menu.addMenuItem(this._volumeMenu);
+ }
+
+ this._network = Main.panel.statusArea.network;
+ if (this._network) {
+ this._networkIcon = this.addIcon(null);
+ this._network.mainIcon.bind_property('gicon', this._networkIcon, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.mainIcon.bind_property('visible', this._networkIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+
+ this._networkSecondaryIcon = this.addIcon(null);
+ this._network.secondaryIcon.bind_property('gicon', this._networkSecondaryIcon, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._network.secondaryIcon.bind_property('visible', this._networkSecondaryIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+
+ this._battery = Main.panel.statusArea.battery;
+ if (this._battery) {
+ this._batteryIcon = this.addIcon(null);
+ this._battery.mainIcon.bind_property('gicon', this._batteryIcon, 'gicon',
+ GObject.BindingFlags.SYNC_CREATE);
+ this._battery.mainIcon.bind_property('visible', this._batteryIcon, 'visible',
+ GObject.BindingFlags.SYNC_CREATE);
+ }
+ },
+
+ setLockedState: function(locked) {
+ this.actor.visible = locked;
+ }
+});
diff --git a/js/ui/status/network.js b/js/ui/status/network.js
index a235bee..72cea77 100644
--- a/js/ui/status/network.js
+++ b/js/ui/status/network.js
@@ -1570,9 +1570,10 @@ const NMApplet = new Lang.Class({
_init: function() {
this.parent('network-offline', _('Network'));
- this._secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn' }));
- this._secondaryIcon.hide();
+ this.secondaryIcon = this.addIcon(new Gio.ThemedIcon({ name: 'network-vpn' }));
+ this.secondaryIcon.hide();
+ this._isLocked = false;
this._client = NMClient.Client.new();
this._statusSection = new PopupMenu.PopupMenuSection();
@@ -1681,12 +1682,8 @@ const NMApplet = new Lang.Class({
},
setLockedState: function(locked) {
- // FIXME: more design discussion is needed before we can
- // expose part of this menu
-
- if (locked)
- this.menu.close();
- this.actor.reactive = !locked;
+ this._isLocked = locked;
+ this._syncNMState();
},
_ensureSource: function() {
@@ -2074,13 +2071,8 @@ const NMApplet = new Lang.Class({
},
_syncNMState: function() {
- if (!this._client.manager_running) {
- log('NetworkManager is not running, hiding...');
- this.menu.close();
- this.actor.hide();
- return;
- } else
- this.actor.show();
+ this.mainIcon.visible = this._client.manager_running;
+ this.actor.visible = this.mainIcon.visible && !this._isLocked;
if (!this._client.networking_enabled) {
this.setIcon('network-offline');
@@ -2192,14 +2184,14 @@ const NMApplet = new Lang.Class({
// only show a separate icon when we're using a wireless/3g connection
if (mc._section == NMConnectionCategory.WIRELESS ||
mc._section == NMConnectionCategory.WWAN) {
- this._secondaryIcon.icon_name = vpnIconName;
- this._secondaryIcon.visible = true;
+ this.secondaryIcon.icon_name = vpnIconName;
+ this.secondaryIcon.show();
} else {
this.setIcon(vpnIconName);
- this._secondaryIcon.visible = false;
+ this.secondaryIcon.hide();
}
} else {
- this._secondaryIcon.visible = false;
+ this.secondaryIcon.hide();
}
// cleanup stale signal connections
diff --git a/js/ui/status/power.js b/js/ui/status/power.js
index 1c18b4d..195422f 100644
--- a/js/ui/status/power.js
+++ b/js/ui/status/power.js
@@ -56,6 +56,7 @@ const Indicator = new Lang.Class({
this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH);
+ this._isLocked = false;
this._deviceItems = [ ];
this._hasPrimary = false;
this._primaryDeviceId = null;
@@ -77,9 +78,8 @@ const Indicator = new Lang.Class({
},
setLockedState: function(locked) {
- if (locked)
- this.menu.close();
- this.actor.reactive = !locked;
+ this._isLocked = locked;
+ this._syncIcon();
},
_readPrimaryDevice: function() {
@@ -150,16 +150,20 @@ const Indicator = new Lang.Class({
}));
},
- _devicesChanged: function() {
+ _syncIcon: function() {
let icon = this._proxy.Icon;
- if (icon) {
+ let hasIcon = (icon != null);
+
+ if (hasIcon) {
let gicon = Gio.icon_new_for_string(icon);
this.setGIcon(gicon);
- this.actor.show();
- } else {
- this.menu.close();
- this.actor.hide();
}
+ this.mainIcon.visible = hasIcon;
+ this.actor.visible = hasIcon && !this._isLocked;
+ },
+
+ _devicesChanged: function() {
+ this._syncIcon();
this._readPrimaryDevice();
this._readOtherDevices();
}
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index 5507887..ab383dc 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -12,14 +12,27 @@ const VOLUME_ADJUSTMENT_STEP = 0.05; /* Volume adjustment step in % */
const VOLUME_NOTIFY_ID = 1;
-const Indicator = new Lang.Class({
- Name: 'VolumeIndicator',
- Extends: PanelMenu.SystemStatusButton,
+// Each Gvc.MixerControl is a connection to PulseAudio,
+// so it's better to make it a singleton
+let _mixerControl;
+function getMixerControl() {
+ if (_mixerControl)
+ return _mixerControl;
- _init: function() {
- this.parent('audio-volume-muted', _("Volume"));
+ _mixerControl = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
+ _mixerControl.open();
+
+ return _mixerControl;
+}
- this._control = new Gvc.MixerControl({ name: 'GNOME Shell Volume Control' });
+const VolumeMenu = new Lang.Class({
+ Name: 'VolumeMenu',
+ Extends: PopupMenu.PopupMenuSection,
+
+ _init: function(control) {
+ this.parent();
+
+ this._control = control;
this._control.connect('state-changed', Lang.bind(this, this._onControlStateChanged));
this._control.connect('default-sink-changed', Lang.bind(this, this._readOutput));
this._control.connect('default-source-changed', Lang.bind(this, this._readInput));
@@ -35,10 +48,10 @@ const Indicator = new Lang.Class({
this._outputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._outputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_output'));
this._outputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
- this.menu.addMenuItem(this._outputTitle);
- this.menu.addMenuItem(this._outputSlider);
+ this.addMenuItem(this._outputTitle);
+ this.addMenuItem(this._outputSlider);
- this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
this._input = null;
this._inputVolumeId = 0;
@@ -47,22 +60,11 @@ const Indicator = new Lang.Class({
this._inputSlider = new PopupMenu.PopupSliderMenuItem(0);
this._inputSlider.connect('value-changed', Lang.bind(this, this._sliderChanged, '_input'));
this._inputSlider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
- this.menu.addMenuItem(this._inputTitle);
- this.menu.addMenuItem(this._inputSlider);
-
- this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
- this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
-
- this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
- this._control.open();
- },
-
- setLockedState: function(locked) {
- this.menu.setSettingsVisibility(!locked);
+ this.addMenuItem(this._inputTitle);
+ this.addMenuItem(this._inputSlider);
},
- _onScrollEvent: function(actor, event) {
- let direction = event.get_scroll_direction();
+ scroll: function(direction) {
let currentVolume = this._output.volume;
if (direction == Clutter.ScrollDirection.DOWN) {
@@ -88,9 +90,8 @@ const Indicator = new Lang.Class({
if (this._control.get_state() == Gvc.MixerControlState.READY) {
this._readOutput();
this._readInput();
- this.actor.show();
} else {
- this.actor.hide();
+ this.emit('icon-changed', null);
}
},
@@ -109,7 +110,7 @@ const Indicator = new Lang.Class({
this._volumeChanged (null, null, '_output');
} else {
this._outputSlider.setValue(0);
- this.setIcon('audio-volume-muted-symbolic');
+ this.emit('icon-changed', 'audio-volume-muted-symbolic');
}
},
@@ -196,15 +197,55 @@ const Indicator = new Lang.Class({
slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
if (property == '_output') {
if (muted)
- this.setIcon('audio-volume-muted');
+ this.emit('icon-changed', 'audio-volume-muted');
else
- this.setIcon(this._volumeToIcon(this._output.volume));
+ this.emit('icon-changed', this._volumeToIcon(this._output.volume));
}
},
_volumeChanged: function(object, param_spec, property) {
this[property+'Slider'].setValue(this[property].volume / this._volumeMax);
if (property == '_output' && !this._output.is_muted)
- this.setIcon(this._volumeToIcon(this._output.volume));
+ this.emit('icon-changed', this._volumeToIcon(this._output.volume));
+ }
+});
+
+const Indicator = new Lang.Class({
+ Name: 'VolumeIndicator',
+ Extends: PanelMenu.SystemStatusButton,
+
+ _init: function() {
+ this.parent('audio-volume-muted', _("Volume"));
+
+ this._isLocked = false;
+
+ this._control = getMixerControl();
+ this._volumeMenu = new VolumeMenu(this._control);
+ this._volumeMenu.connect('icon-changed', Lang.bind(this, function(menu, icon) {
+ this._hasPulseAudio = (icon != null);
+ this.setIcon(icon);
+ this._syncVisibility();
+ }));
+
+ this.menu.addMenuItem(this._volumeMenu);
+
+ this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this.menu.addSettingsAction(_("Sound Settings"), 'gnome-sound-panel.desktop');
+
+ this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
+ },
+
+ setLockedState: function(locked) {
+ this._isLocked = locked;
+ this._syncVisibility();
+ },
+
+ _syncVisibility: function() {
+ this.actor.visible = this._hasPulseAudio && !this._isLocked;
+ this.mainIcon.visible = this._hasPulseAudio;
+ },
+
+ _onScrollEvent: function(actor, event) {
+ this._volumeMenu.scroll(event.get_scroll_direction());
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]