104071acbd
Every since commit aa394754
, StBoxLayout has supported ClutterActor's
expand/align properties in addition to the container-specific child
properties. Given that that's the only container left with a special
child meta, it's time to fully embrace the generic properties (and
eventually remove the child meta).
https://gitlab.gnome.org/GNOME/gnome-shell/merge_requests/780
195 lines
6.4 KiB
JavaScript
195 lines
6.4 KiB
JavaScript
/* exported AudioDeviceSelectionDBus */
|
|
const { Clutter, Gio, GLib, GObject, Meta, Shell, St } = imports.gi;
|
|
|
|
const Main = imports.ui.main;
|
|
const ModalDialog = imports.ui.modalDialog;
|
|
|
|
const { loadInterfaceXML } = imports.misc.fileUtils;
|
|
|
|
var AudioDevice = {
|
|
HEADPHONES: 1 << 0,
|
|
HEADSET: 1 << 1,
|
|
MICROPHONE: 1 << 2
|
|
};
|
|
|
|
const AudioDeviceSelectionIface = loadInterfaceXML('org.gnome.Shell.AudioDeviceSelection');
|
|
|
|
var AudioDeviceSelectionDialog = GObject.registerClass({
|
|
Signals: { 'device-selected': { param_types: [GObject.TYPE_UINT] } }
|
|
}, class AudioDeviceSelectionDialog extends ModalDialog.ModalDialog {
|
|
_init(devices) {
|
|
super._init({ styleClass: 'audio-device-selection-dialog' });
|
|
|
|
this._deviceItems = {};
|
|
|
|
this._buildLayout();
|
|
|
|
if (devices & AudioDevice.HEADPHONES)
|
|
this._addDevice(AudioDevice.HEADPHONES);
|
|
if (devices & AudioDevice.HEADSET)
|
|
this._addDevice(AudioDevice.HEADSET);
|
|
if (devices & AudioDevice.MICROPHONE)
|
|
this._addDevice(AudioDevice.MICROPHONE);
|
|
|
|
if (this._selectionBox.get_n_children() < 2)
|
|
throw new Error('Too few devices for a selection');
|
|
}
|
|
|
|
_buildLayout() {
|
|
let title = new St.Label({ style_class: 'audio-selection-title',
|
|
text: _("Select Audio Device"),
|
|
x_align: Clutter.ActorAlign.CENTER });
|
|
|
|
this.contentLayout.style_class = 'audio-selection-content';
|
|
this.contentLayout.add(title);
|
|
|
|
this._selectionBox = new St.BoxLayout({
|
|
style_class: 'audio-selection-box',
|
|
x_expand: true,
|
|
});
|
|
this.contentLayout.add_child(this._selectionBox);
|
|
|
|
if (Main.sessionMode.allowSettings)
|
|
this.addButton({ action: this._openSettings.bind(this),
|
|
label: _("Sound Settings") });
|
|
this.addButton({ action: this.close.bind(this),
|
|
label: _("Cancel"),
|
|
key: Clutter.Escape });
|
|
}
|
|
|
|
_getDeviceLabel(device) {
|
|
switch (device) {
|
|
case AudioDevice.HEADPHONES:
|
|
return _("Headphones");
|
|
case AudioDevice.HEADSET:
|
|
return _("Headset");
|
|
case AudioDevice.MICROPHONE:
|
|
return _("Microphone");
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
_getDeviceIcon(device) {
|
|
switch (device) {
|
|
case AudioDevice.HEADPHONES:
|
|
return 'audio-headphones-symbolic';
|
|
case AudioDevice.HEADSET:
|
|
return 'audio-headset-symbolic';
|
|
case AudioDevice.MICROPHONE:
|
|
return 'audio-input-microphone-symbolic';
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
_addDevice(device) {
|
|
let box = new St.BoxLayout({ style_class: 'audio-selection-device-box',
|
|
vertical: true });
|
|
box.connect('notify::height', () => {
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, () => {
|
|
box.width = box.height;
|
|
return GLib.SOURCE_REMOVE;
|
|
});
|
|
});
|
|
|
|
let icon = new St.Icon({ style_class: 'audio-selection-device-icon',
|
|
icon_name: this._getDeviceIcon(device) });
|
|
box.add(icon);
|
|
|
|
let label = new St.Label({ style_class: 'audio-selection-device-label',
|
|
text: this._getDeviceLabel(device),
|
|
x_align: Clutter.ActorAlign.CENTER });
|
|
box.add(label);
|
|
|
|
let button = new St.Button({ style_class: 'audio-selection-device',
|
|
can_focus: true,
|
|
child: box });
|
|
this._selectionBox.add(button);
|
|
|
|
button.connect('clicked', () => {
|
|
this.emit('device-selected', device);
|
|
this.close();
|
|
Main.overview.hide();
|
|
});
|
|
}
|
|
|
|
_openSettings() {
|
|
let desktopFile = 'gnome-sound-panel.desktop';
|
|
let app = Shell.AppSystem.get_default().lookup_app(desktopFile);
|
|
|
|
if (!app) {
|
|
log(`Settings panel for desktop file ${desktopFile} could not be loaded!`);
|
|
return;
|
|
}
|
|
|
|
this.close();
|
|
Main.overview.hide();
|
|
app.activate();
|
|
}
|
|
});
|
|
|
|
var AudioDeviceSelectionDBus = class AudioDeviceSelectionDBus {
|
|
constructor() {
|
|
this._audioSelectionDialog = null;
|
|
|
|
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(AudioDeviceSelectionIface, this);
|
|
this._dbusImpl.export(Gio.DBus.session, '/org/gnome/Shell/AudioDeviceSelection');
|
|
|
|
Gio.DBus.session.own_name('org.gnome.Shell.AudioDeviceSelection', Gio.BusNameOwnerFlags.REPLACE, null, null);
|
|
}
|
|
|
|
_onDialogClosed() {
|
|
this._audioSelectionDialog = null;
|
|
}
|
|
|
|
_onDeviceSelected(dialog, device) {
|
|
let connection = this._dbusImpl.get_connection();
|
|
let info = this._dbusImpl.get_info();
|
|
let deviceName = Object.keys(AudioDevice).filter(
|
|
dev => AudioDevice[dev] == device
|
|
)[0].toLowerCase();
|
|
connection.emit_signal(this._audioSelectionDialog._sender,
|
|
this._dbusImpl.get_object_path(),
|
|
info ? info.name : null,
|
|
'DeviceSelected',
|
|
GLib.Variant.new('(s)', [deviceName]));
|
|
}
|
|
|
|
OpenAsync(params, invocation) {
|
|
if (this._audioSelectionDialog) {
|
|
invocation.return_value(null);
|
|
return;
|
|
}
|
|
|
|
let [deviceNames] = params;
|
|
let devices = 0;
|
|
deviceNames.forEach(n => (devices |= AudioDevice[n.toUpperCase()]));
|
|
|
|
let dialog;
|
|
try {
|
|
dialog = new AudioDeviceSelectionDialog(devices);
|
|
} catch (e) {
|
|
invocation.return_value(null);
|
|
return;
|
|
}
|
|
dialog._sender = invocation.get_sender();
|
|
|
|
dialog.connect('closed', this._onDialogClosed.bind(this));
|
|
dialog.connect('device-selected',
|
|
this._onDeviceSelected.bind(this));
|
|
dialog.open();
|
|
|
|
this._audioSelectionDialog = dialog;
|
|
invocation.return_value(null);
|
|
}
|
|
|
|
CloseAsync(params, invocation) {
|
|
if (this._audioSelectionDialog &&
|
|
this._audioSelectionDialog._sender == invocation.get_sender())
|
|
this._audioSelectionDialog.close();
|
|
|
|
invocation.return_value(null);
|
|
}
|
|
};
|