diff --git a/schemas/net.evermiss.mymindstorm.volume-mixer.gschema.xml b/schemas/net.evermiss.mymindstorm.volume-mixer.gschema.xml index 7a88ae6..49b8b63 100644 --- a/schemas/net.evermiss.mymindstorm.volume-mixer.gschema.xml +++ b/schemas/net.evermiss.mymindstorm.volume-mixer.gschema.xml @@ -10,6 +10,9 @@ true + + false + diff --git a/src/applicationStreamSlider.js b/src/applicationStreamSlider.js index 2e34c29..4e26467 100644 --- a/src/applicationStreamSlider.js +++ b/src/applicationStreamSlider.js @@ -6,31 +6,36 @@ const { BoxLayout, Label } = imports.gi.St; const Volume = imports.ui.status.volume; export class ApplicationStreamSlider extends Volume.StreamSlider { - constructor(stream, opts) { - super(Volume.getMixerControl()); + constructor(stream, opts) { + super(Volume.getMixerControl()); - this.stream = stream; + this.stream = stream; - if (opts.showIcon) { - this._icon.icon_name = stream.get_icon_name(); - } + if (opts.showIcon) { + this._icon.icon_name = stream.get_icon_name(); + } - let name = stream.get_name(); - let description = stream.get_description(); + let name = stream.get_name(); + let description = stream.get_description(); - if (name || description) { - this._vbox = new BoxLayout() - this._vbox.vertical = true; + if (name || description) { + this._vbox = new BoxLayout() + this._vbox.vertical = true; - this._label = new Label(); - this._label.text = name && opts.showDesc ? `${name} - ${description}` : (name || description); - this._vbox.add(this._label); + this._label = new Label(); + this._label.text = name && opts.showDesc ? `${name} - ${description}` : (name || description); + this._vbox.add(this._label); - this.item.remove_child(this._slider); - this._vbox.add(this._slider); - this._slider.set_height(32); + this.item.remove_child(this._slider); + this._vbox.add(this._slider); + this._slider.set_height(32); - this.item.actor.add(this._vbox); + this.item.actor.add(this._vbox); + } + } + + destroy() { + this._disconnectStream(this._stream); + this._stream = null; } - } }; diff --git a/src/volumeMixerPopupMenu.js b/src/volumeMixerPopupMenu.js index 8d0d0b7..0a66bf3 100644 --- a/src/volumeMixerPopupMenu.js +++ b/src/volumeMixerPopupMenu.js @@ -15,18 +15,8 @@ const Me = ExtensionUtils.getCurrentExtension(); export class VolumeMixerPopupMenu extends PopupMenu.PopupMenuSection { constructor() { super(); - this._applicationStreams = {}; - - // The PopupSeparatorMenuItem needs something above and below it or it won't display - this._hiddenItem = new PopupMenu.PopupBaseMenuItem(); - this._hiddenItem.set_height(0) - this.addMenuItem(this._hiddenItem); - - this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); - this._control = Volume.getMixerControl(); - this._streamAddedEventId = this._control.connect("stream-added", this._streamAdded.bind(this)); - this._streamRemovedEventId = this._control.connect("stream-removed", this._streamRemoved.bind(this)); + this._applicationStreams = {}; let gschema = SettingsSchemaSource.new_from_directory( Me.dir.get_child('schemas').get_path(), @@ -38,11 +28,56 @@ export class VolumeMixerPopupMenu extends PopupMenu.PopupMenuSection { settings_schema: gschema.lookup('net.evermiss.mymindstorm.volume-mixer', true) }); + this._block = null; + this._blockMenu = null; + this._dummyStream = null; + + this._control = Volume.getMixerControl(); + this._streamAddedEventId = this._control.connect("stream-added", this._streamAdded.bind(this)); + this._streamRemovedEventId = this._control.connect("stream-removed", this._streamRemoved.bind(this)); + this._settingsChangedId = this.settings.connect('changed', () => this._updateStreams()); this._updateStreams(); } + _addDummyIfNoStreams() { + if (this._dummyStream) { + return; + } + + if (this._getStreamCount() !== 0) { + return; + } + + this._dummyStream = new PopupMenu.PopupMenuItem("There are currently no audio streams", { + activate: false, + reactive: false + }); + this._blockMenu.addMenuItem(this._dummyStream); + } + + _createMenu() { + if (this._old_menu) { + this._block = new PopupMenu.PopupMenuSection(); + this._blockMenu = this._block; + + // The PopupSeparatorMenuItem needs something above and below it or it won't display + this._hiddenItem = new PopupMenu.PopupBaseMenuItem(); + this._hiddenItem.set_height(0) + this._block.addMenuItem(this._hiddenItem); + + this._block.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); + + } else { + this._block = new PopupMenu.PopupSubMenuMenuItem('Volume Mixer', true); + this._block.icon.icon_name = 'audio-volume-high-symbolic'; + this._blockMenu = this._block.menu; + } + + this.addMenuItem(this._block); + } + _streamAdded(control, id) { if (id in this._applicationStreams) { return; @@ -65,36 +100,70 @@ export class VolumeMixerPopupMenu extends PopupMenu.PopupMenuSection { } this._applicationStreams[id] = new ApplicationStreamSlider(stream, { showDesc: this._showStreamDesc, showIcon: this._showStreamIcon }); - this.addMenuItem(this._applicationStreams[id].item); + + if (this._dummyStream) { + this._dummyStream.destroy(); + this._dummyStream = null; + } + + this._blockMenu.addMenuItem(this._applicationStreams[id].item); } _streamRemoved(_control, id) { if (id in this._applicationStreams) { this._applicationStreams[id].item.destroy(); delete this._applicationStreams[id]; + + if (!this._old_menu) { + this._addDummyIfNoStreams(); + } } } _updateStreams() { for (const id in this._applicationStreams) { this._applicationStreams[id].item.destroy(); + this._applicationStreams[id].destroy(); delete this._applicationStreams[id]; } + if (this._block) { + this._block.destroy(); + this._block = null; + this._blockMenu = null; + } + + this._dummyStream = null; + this._filteredApps = this.settings.get_strv("filtered-apps"); this._filterMode = this.settings.get_string("filter-mode"); this._showStreamDesc = this.settings.get_boolean("show-description"); this._showStreamIcon = this.settings.get_boolean("show-icon"); + this._old_menu = this.settings.get_boolean("old-menu"); + + this._createMenu(); for (const stream of this._control.get_streams()) { this._streamAdded(this._control, stream.get_id()) } + + if (!this._old_menu) { + this._addDummyIfNoStreams(); + } + } + + _getStreamCount() { + return Object.keys(this._applicationStreams).length; } destroy() { this._control.disconnect(this._streamAddedEventId); this._control.disconnect(this._streamRemovedEventId); this.settings.disconnect(this._settingsChangedId); + if (this._dummyStream) { + this._dummyStream.destroy(); + } + this._block.destroy(); super.destroy(); } }; diff --git a/src/volumeMixerPrefsPage.js b/src/volumeMixerPrefsPage.js index ac018d0..ee5f451 100644 --- a/src/volumeMixerPrefsPage.js +++ b/src/volumeMixerPrefsPage.js @@ -60,6 +60,30 @@ export const VolumeMixerPrefsPage = GObject.registerClass({ showIconRow.add_suffix(showIconToggle); showIconRow.activatable_widget = showIconToggle; + // old-menu + const oldMenuRow = new Adw.ActionRow({ + title: 'Show In Old Way', + subtitle: 'Will show all controlls in menu instead of using Volume Mixer Sub Menu. ' + + '\x0ABeaware that if there are too many audio sources, this option will ' + + 'overflow the menu' + }); + generalGroup.add(oldMenuRow); + + const oldMenuToggle = new Gtk.Switch({ + active: this.settings.get_boolean('old-menu'), + valign: Gtk.Align.CENTER + }); + + this.settings.bind( + 'old-menu', + oldMenuToggle, + 'active', + Gio.SettingsBindFlags.DEFAULT + ); + + oldMenuRow.add_suffix(oldMenuToggle); + oldMenuRow.activatable_widget = oldMenuToggle; + // Application filter settings group const filterGroup = new Adw.PreferencesGroup({ title: 'Application Filtering',