[gnome-shell] volume: Clean up stream/slider handling code
- From: Jasper St. Pierre <jstpierre src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-shell] volume: Clean up stream/slider handling code
- Date: Fri, 21 Dec 2012 18:26:32 +0000 (UTC)
commit c9d0e82c52d6033ee70583717d91c3d47f6f7e9d
Author: Jasper St. Pierre <jstpierre mecheye net>
Date: Wed Dec 19 21:57:26 2012 -0500
volume: Clean up stream/slider handling code
Rather than using naming schemes and dynamic property lookups as
a kind of namespace, use what was designed to be used as a namespace:
a class.
https://bugzilla.gnome.org/show_bug.cgi?id=690539
js/ui/status/volume.js | 316 +++++++++++++++++++++++++++++-------------------
1 files changed, 193 insertions(+), 123 deletions(-)
---
diff --git a/js/ui/status/volume.js b/js/ui/status/volume.js
index b1774fc..b08f3d6 100644
--- a/js/ui/status/volume.js
+++ b/js/ui/status/volume.js
@@ -5,6 +5,7 @@ const Lang = imports.lang;
const Gio = imports.gi.Gio;
const Gvc = imports.gi.Gvc;
const St = imports.gi.St;
+const Signals = imports.signals;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
@@ -26,61 +27,134 @@ function getMixerControl() {
return _mixerControl;
}
-const VolumeMenu = new Lang.Class({
- Name: 'VolumeMenu',
- Extends: PopupMenu.PopupMenuSection,
+const StreamSlider = new Lang.Class({
+ Name: 'StreamSlider',
- _init: function(control) {
- this.parent();
+ _init: function(control, title) {
+ this._control = control;
- this._hasHeadphones = false;
+ this.item = new PopupMenu.PopupMenuSection();
- 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));
- this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
- this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
- this._volumeMax = this._control.get_vol_max_norm();
+ this._title = new PopupMenu.PopupMenuItem(title, { reactive: false });
+ this._slider = new PopupMenu.PopupSliderMenuItem(0);
+ this._slider.connect('value-changed', Lang.bind(this, this._sliderChanged));
+ this._slider.connect('drag-end', Lang.bind(this, this._notifyVolumeChange));
- this._output = null;
- this._outputVolumeId = 0;
- this._outputMutedId = 0;
- /* Translators: This is the label for audio volume */
- this._outputTitle = new PopupMenu.PopupMenuItem(_("Volume"), { reactive: false });
- 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.addMenuItem(this._outputTitle);
- this.addMenuItem(this._outputSlider);
+ this.item.addMenuItem(this._title);
+ this.item.addMenuItem(this._slider);
- this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+ this._stream = null;
+ this._shouldShow = true;
+ },
- this._input = null;
- this._inputVolumeId = 0;
- this._inputMutedId = 0;
- this._inputTitle = new PopupMenu.PopupMenuItem(_("Microphone"), { reactive: false });
- 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.addMenuItem(this._inputTitle);
- this.addMenuItem(this._inputSlider);
+ get stream() {
+ return this._stream;
+ },
- this._onControlStateChanged();
+ set stream(stream) {
+ if (this._stream) {
+ this._disconnectStream(this._stream);
+ }
+
+ this._stream = stream;
+
+ if (this._stream) {
+ this._connectStream(this._stream);
+ this._updateVolume();
+ } else {
+ this.emit('stream-updated');
+ }
+
+ this._updateVisibility();
+ },
+
+ _disconnectStream: function(stream) {
+ stream.disconnect(this._mutedChangedId);
+ this._mutedChangedId = 0;
+ stream.disconnect(this._volumeChangedId);
+ this._volumeChangedId = 0;
+ },
+
+ _connectStream: function(stream) {
+ this._mutedChangedId = stream.connect('notify::is-muted', Lang.bind(this, this._updateVolume));
+ this._volumeChangedId = stream.connect('notify::volume', Lang.bind(this, this._updateVolume));
+ },
+
+ _shouldBeVisible: function() {
+ return this._stream != null;
+ },
+
+ _updateVisibility: function() {
+ let visible = this._shouldBeVisible();
+ this._title.actor.visible = visible;
+ this._slider.actor.visible = visible;
},
scroll: function(event) {
- this._outputSlider.scroll(event);
+ this._slider.scroll(event);
},
- _onControlStateChanged: function() {
- if (this._control.get_state() == Gvc.MixerControlState.READY) {
- this._readOutput();
- this._readInput();
- this._maybeShowInput();
+ setValue: function(value) {
+ // piggy-back off of sliderChanged
+ this._slider.setValue(value);
+ },
+
+ _sliderChanged: function(slider, value, property) {
+ if (!this._stream)
+ return;
+
+ let volume = value * this._control.get_vol_max_norm();
+ let prevMuted = this._stream.is_muted;
+ if (volume < 1) {
+ this._stream.volume = 0;
+ if (!prevMuted)
+ this._stream.change_is_muted(true);
} else {
- this.emit('icon-changed');
+ this._stream.volume = volume;
+ if (prevMuted)
+ this._stream.change_is_muted(false);
}
+ this._stream.push_volume();
+ },
+
+ _notifyVolumeChange: function() {
+ global.cancel_theme_sound(VOLUME_NOTIFY_ID);
+ global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
+ },
+
+ _updateVolume: function() {
+ let muted = this._stream.is_muted;
+ this._slider.setValue(muted ? 0 : (this._stream.volume / this._control.get_vol_max_norm()));
+ this.emit('stream-updated');
+ },
+
+ getIcon: function() {
+ if (!this._stream)
+ return null;
+
+ let volume = this._stream.volume;
+ if (this._stream.is_muted || volume <= 0) {
+ return 'audio-volume-muted-symbolic';
+ } else {
+ let n = Math.floor(3 * volume / this._control.get_vol_max_norm()) + 1;
+ if (n < 2)
+ return 'audio-volume-low-symbolic';
+ if (n >= 3)
+ return 'audio-volume-high-symbolic';
+ return 'audio-volume-medium-symbolic';
+ }
+ }
+});
+Signals.addSignalMethods(StreamSlider.prototype);
+
+const OutputStreamSlider = new Lang.Class({
+ Name: 'OutputStreamSlider',
+ Extends: StreamSlider,
+
+ _connectStream: function(stream) {
+ this.parent(stream);
+ this._portChangedId = stream.connect('notify::port', Lang.bind(this, this._portChanged));
+ this._portChanged();
},
_findHeadphones: function(sink) {
@@ -99,58 +173,41 @@ const VolumeMenu = new Lang.Class({
return false;
},
- _portChanged: function() {
- this._hasHeadphones = this._findHeadphones(this._output);
- this.emit('headphones-changed', this._hasHeadphones);
+ _disconnectStream: function(stream) {
+ this.parent(stream);
+ stream.disconnect(this._portChangedId);
+ this._portChangedId = 0;
},
- _readOutput: function() {
- if (this._outputVolumeId) {
- this._output.disconnect(this._outputVolumeId);
- this._output.disconnect(this._outputMutedId);
- this._output.disconnect(this._outputPortId);
- this._outputVolumeId = 0;
- this._outputMutedId = 0;
- this._outputPortId = 0;
- }
- this._output = this._control.get_default_sink();
- if (this._output) {
- this._outputMutedId = this._output.connect('notify::is-muted', Lang.bind(this, this._updateVolume, '_output'));
- this._outputVolumeId = this._output.connect('notify::volume', Lang.bind(this, this._updateVolume, '_output'));
- this._outputPortId = this._output.connect('notify::port', Lang.bind(this, this._portChanged));
-
- this._updateVolume(null, null, '_output');
- this._portChanged();
- } else {
- this.hasHeadphones = false;
- this._outputSlider.setValue(0);
- this.emit('icon-changed');
+ _portChanged: function() {
+ let hasHeadphones = this._findHeadphones(this._stream);
+ if (hasHeadphones != this._hasHeadphones) {
+ this._hasHeadphones = hasHeadphones;
+ this.emit('headphones-changed', this._hasHeadphones);
}
+ }
+});
+
+const InputStreamSlider = new Lang.Class({
+ Name: 'InputStreamSlider',
+ Extends: StreamSlider,
+
+ _init: function(control, title) {
+ this.parent(control, title);
+ this._control.connect('stream-added', Lang.bind(this, this._maybeShowInput));
+ this._control.connect('stream-removed', Lang.bind(this, this._maybeShowInput));
},
- _readInput: function() {
- if (this._inputVolumeId) {
- this._input.disconnect(this._inputVolumeId);
- this._input.disconnect(this._inputMutedId);
- this._inputVolumeId = 0;
- this._inputMutedId = 0;
- }
- this._input = this._control.get_default_source();
- if (this._input) {
- this._inputMutedId = this._input.connect('notify::is-muted', Lang.bind(this, this._updateVolume, '_input'));
- this._inputVolumeId = this._input.connect('notify::volume', Lang.bind(this, this._updateVolume, '_input'));
- this._updateVolume(null, null, '_input');
- } else {
- this._inputTitle.actor.hide();
- this._inputSlider.actor.hide();
- }
+ _connectStream: function(stream) {
+ this.parent(stream);
+ this._maybeShowInput();
},
_maybeShowInput: function() {
// only show input widgets if any application is recording audio
let showInput = false;
let recordingApps = this._control.get_source_outputs();
- if (this._input && recordingApps) {
+ if (this._stream && recordingApps) {
for (let i = 0; i < recordingApps.length; i++) {
let outputStream = recordingApps[i];
let id = outputStream.get_application_id();
@@ -163,57 +220,70 @@ const VolumeMenu = new Lang.Class({
}
}
- this._inputTitle.actor.visible = showInput;
- this._inputSlider.actor.visible = showInput;
+ this._showInput = showInput;
+ this._updateVisibility();
},
- _sliderChanged: function(slider, value, property) {
- if (this[property] == null) {
- log ('Volume slider changed for %s, but %s does not exist'.format(property, property));
- return;
- }
- let volume = value * this._volumeMax;
- let prev_muted = this[property].is_muted;
- if (volume < 1) {
- this[property].volume = 0;
- if (!prev_muted)
- this[property].change_is_muted(true);
- } else {
- this[property].volume = volume;
- if (prev_muted)
- this[property].change_is_muted(false);
- }
- this[property].push_volume();
- },
+ _shouldBeVisible: function() {
+ return this.parent() && this._showInput;
+ }
+});
- _notifyVolumeChange: function() {
- global.cancel_theme_sound(VOLUME_NOTIFY_ID);
- global.play_theme_sound(VOLUME_NOTIFY_ID, 'audio-volume-change');
+const VolumeMenu = new Lang.Class({
+ Name: 'VolumeMenu',
+ Extends: PopupMenu.PopupMenuSection,
+
+ _init: function(control) {
+ this.parent();
+
+ this.hasHeadphones = false;
+
+ 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));
+
+ /* Translators: This is the label for audio volume */
+ this._output = new OutputStreamSlider(this._control, _("Volume"));
+ this._output.connect('stream-updated', Lang.bind(this, function() {
+ this.emit('icon-changed');
+ }));
+ this._output.connect('headphones-changed', Lang.bind(this, function(stream, value) {
+ this.emit('headphones-changed', value);
+ }));
+ this.addMenuItem(this._output.item);
+
+ this._input = new InputStreamSlider(this._control, _("Microphone"));
+ this.addMenuItem(this._input.item);
+
+ this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
+
+ this._onControlStateChanged();
},
- getIcon: function() {
- if (!this._output)
- return null;
+ scroll: function(event) {
+ this._output.scroll(event);
+ },
- let volume = this._output.volume;
- if (this._output.is_muted || volume <= 0) {
- return 'audio-volume-muted-symbolic';
+ _onControlStateChanged: function() {
+ if (this._control.get_state() == Gvc.MixerControlState.READY) {
+ this._readInput();
+ this._readOutput();
} else {
- let n = Math.floor(3 * volume / this._volumeMax) + 1;
- if (n < 2)
- return 'audio-volume-low-symbolic';
- if (n >= 3)
- return 'audio-volume-high-symbolic';
- return 'audio-volume-medium-symbolic';
+ this.emit('icon-changed');
}
},
- _updateVolume: function(object, param_spec, property) {
- let muted = this[property].is_muted;
- let slider = this[property+'Slider'];
- slider.setValue(muted ? 0 : (this[property].volume / this._volumeMax));
- if (property == '_output')
- this.emit('icon-changed');
+ _readOutput: function() {
+ this._output.stream = this._control.get_default_sink();
+ },
+
+ _readInput: function() {
+ this._input.stream = this._control.get_default_source();
+ },
+
+ getIcon: function() {
+ return this._output.getIcon();
}
});
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]